From fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc Mon Sep 17 00:00:00 2001 From: Jordi Vilalta Prat Date: Sun, 15 Feb 2009 06:10:59 +0000 Subject: Import the SCI engine sources from the FreeSCI Glutton branch (it doesn't compile yet) svn-id: r38192 --- base/plugins.cpp | 3 + configure | 1 + engines/engines.mk | 5 + engines/sci/Makefile.am | 24 + engines/sci/README | 19 + engines/sci/config.c | 3439 ++++++++++ engines/sci/config.l | 1116 +++ engines/sci/config/Makefile.am | 13 + engines/sci/config/config.l | 702 ++ engines/sci/config/config.test | 48 + engines/sci/config/dynlink.c | 69 + engines/sci/config/extension.c | 66 + engines/sci/config/lsl2.scifx | 43 + engines/sci/config/option-test.conf | 1 + engines/sci/config/sq3.scifx | 81 + engines/sci/config/test-options.c | 91 + engines/sci/config/test-parse.c | 59 + engines/sci/dc/3dutils.c | 217 + engines/sci/dc/Makefile | 168 + engines/sci/dc/aica.h | 52 + engines/sci/dc/aica_cmd_iface.h | 155 + engines/sci/dc/aica_crt0.s | 149 + engines/sci/dc/aica_main.c | 207 + engines/sci/dc/aica_sup.c | 242 + engines/sci/dc/bkg.c | 133 + engines/sci/dc/config.h.in | 174 + engines/sci/dc/dc.h | 88 + engines/sci/dc/dc_save.c | 548 ++ engines/sci/dc/gamemenu.c | 702 ++ engines/sci/dc/gp.h | 90 + engines/sci/dc/keyboard.c | 5077 ++++++++++++++ engines/sci/dc/keyboard.h | 67 + engines/sci/dc/mouse1.h | 44 + engines/sci/dc/options.h | 51 + engines/sci/dc/selectgame.c | 156 + engines/sci/dc/selectgame.h | 40 + engines/sci/dc/sintab.h | 73 + engines/sci/dc/snd_stream.c | 537 ++ engines/sci/dc/stream.h | 106 + engines/sci/dc/texture.c | 73 + engines/sci/engine/Makefile.am | 11 + engines/sci/engine/cfsml.pl | 1182 ++++ engines/sci/engine/game.c | 787 +++ engines/sci/engine/gc.c | 334 + engines/sci/engine/gc.h | 41 + engines/sci/engine/grammar.c | 720 ++ engines/sci/engine/heap.c | 298 + engines/sci/engine/heap.h | 125 + engines/sci/engine/kemu_old.c | 330 + engines/sci/engine/kernel.c | 1092 +++ engines/sci/engine/kernel_compat.h | 77 + engines/sci/engine/kernel_types.h | 96 + engines/sci/engine/kevent.c | 229 + engines/sci/engine/kfile.c | 1174 ++++ engines/sci/engine/kgraphics.c | 3627 ++++++++++ engines/sci/engine/klists.c | 608 ++ engines/sci/engine/kmath.c | 201 + engines/sci/engine/kmenu.c | 533 ++ engines/sci/engine/kmovement.c | 580 ++ engines/sci/engine/kpathing.c | 1734 +++++ engines/sci/engine/kscripts.c | 353 + engines/sci/engine/ksound.c | 948 +++ engines/sci/engine/kstring.c | 802 +++ engines/sci/engine/makefile.dos | 22 + engines/sci/engine/message.c | 226 + engines/sci/engine/message.h | 83 + engines/sci/engine/said.c | 2561 +++++++ engines/sci/engine/said.y | 950 +++ engines/sci/engine/savegame.c | 4894 +++++++++++++ engines/sci/engine/savegame.cfsml | 1226 ++++ engines/sci/engine/sci_graphics.h | 44 + engines/sci/engine/scriptconsole.c | 1342 ++++ engines/sci/engine/scriptdebug.c | 3887 +++++++++++ engines/sci/engine/seg_manager.c | 2063 ++++++ engines/sci/engine/simplesaid.c | 312 + engines/sci/engine/sys_strings.c | 119 + engines/sci/engine/vm.c | 2410 +++++++ engines/sci/gfx/Makefile.am | 20 + engines/sci/gfx/alpha_mvi_crossblit.c | 360 + engines/sci/gfx/antialias.c | 163 + engines/sci/gfx/drivers/EXPORTS_ggi | 1 + engines/sci/gfx/drivers/EXPORTS_sdl | 1 + engines/sci/gfx/drivers/EXPORTS_xlib | 2 + engines/sci/gfx/drivers/Makefile.am | 32 + engines/sci/gfx/drivers/dc_driver.c | 1267 ++++ engines/sci/gfx/drivers/dd_driver.cpp | 1021 +++ engines/sci/gfx/drivers/dd_driver.h | 2 + engines/sci/gfx/drivers/dd_driver_line.cpp | 163 + engines/sci/gfx/drivers/directfb_driver.c | 1033 +++ engines/sci/gfx/drivers/dx_driver.cpp | 1179 ++++ engines/sci/gfx/drivers/dx_driver.h | 151 + engines/sci/gfx/drivers/gfx_drivers.c | 192 + engines/sci/gfx/drivers/ggi_driver.c | 1035 +++ engines/sci/gfx/drivers/null_driver.c | 211 + engines/sci/gfx/drivers/scummvm_driver.cpp | 543 ++ engines/sci/gfx/drivers/sdl_driver.c | 1235 ++++ engines/sci/gfx/drivers/xlib_driver.c | 1460 ++++ engines/sci/gfx/font-5x8.c | 2580 +++++++ engines/sci/gfx/font-6x10.c | 3092 +++++++++ engines/sci/gfx/font.c | 385 ++ engines/sci/gfx/gfx_console.c | 1450 ++++ engines/sci/gfx/gfx_crossblit.c | 107 + engines/sci/gfx/gfx_line.c | 96 + engines/sci/gfx/gfx_pixmap_scale.c | 497 ++ engines/sci/gfx/gfx_res_options.c | 652 ++ engines/sci/gfx/gfx_resource.c | 434 ++ engines/sci/gfx/gfx_support.c | 450 ++ engines/sci/gfx/gfx_test.c | 1504 ++++ engines/sci/gfx/gfx_tools.c | 465 ++ engines/sci/gfx/menubar.c | 536 ++ engines/sci/gfx/operations.c | 2490 +++++++ engines/sci/gfx/resmgr.c | 708 ++ engines/sci/gfx/resource/Makefile.am | 7 + engines/sci/gfx/resource/sci_cursor_0.c | 107 + engines/sci/gfx/resource/sci_font.c | 153 + engines/sci/gfx/resource/sci_pal_1.c | 178 + engines/sci/gfx/resource/sci_pic_0.c | 2023 ++++++ engines/sci/gfx/resource/sci_picfill.c | 429 ++ engines/sci/gfx/resource/sci_picfill_aux.c | 205 + engines/sci/gfx/resource/sci_resmgr.c | 341 + engines/sci/gfx/resource/sci_view_0.c | 254 + engines/sci/gfx/resource/sci_view_1.c | 554 ++ engines/sci/gfx/sbtree.c | 473 ++ engines/sci/gfx/sci_widgets.c | 765 +++ engines/sci/gfx/widgets.c | 2600 +++++++ engines/sci/gfx/wrapper.c | 26 + engines/sci/include/Makefile.am | 19 + engines/sci/include/aatree.h | 85 + engines/sci/include/beos/Makefile.am | 1 + engines/sci/include/beos/collsyms.h | 129 + engines/sci/include/beos/fnmatch.h | 49 + engines/sci/include/conf_driver.h | 204 + engines/sci/include/conf_extension.h | 71 + engines/sci/include/conf_parse.h | 108 + engines/sci/include/conf_subsystems.h | 80 + engines/sci/include/conf_summary.h | 106 + engines/sci/include/console.h | 254 + engines/sci/include/engine.h | 306 + engines/sci/include/event.h | 15 + engines/sci/include/game_select.h | 38 + engines/sci/include/gfx_driver.h | 438 ++ engines/sci/include/gfx_operations.h | 733 ++ engines/sci/include/gfx_options.h | 86 + engines/sci/include/gfx_res_options.h | 134 + engines/sci/include/gfx_resmgr.h | 354 + engines/sci/include/gfx_resource.h | 440 ++ engines/sci/include/gfx_state_internal.h | 238 + engines/sci/include/gfx_system.h | 445 ++ engines/sci/include/gfx_tools.h | 293 + engines/sci/include/gfx_widgets.h | 548 ++ engines/sci/include/hashmap.h | 132 + engines/sci/include/heapmgr.h | 120 + engines/sci/include/int_hashmap.h | 63 + engines/sci/include/kdebug.h | 121 + engines/sci/include/kernel.h | 403 ++ engines/sci/include/list.h | 162 + engines/sci/include/listener.h | 38 + engines/sci/include/menubar.h | 219 + engines/sci/include/modules.h | 84 + engines/sci/include/old_objects.h | 54 + engines/sci/include/reg_t_hashmap.h | 71 + engines/sci/include/resource.h | 510 ++ engines/sci/include/sbtree.h | 114 + engines/sci/include/sci_conf.h | 187 + engines/sci/include/sci_dos.h | 174 + engines/sci/include/sci_memory.h | 381 + engines/sci/include/sci_midi.h | 53 + engines/sci/include/sci_widgets.h | 211 + engines/sci/include/sciresource.h | 538 ++ engines/sci/include/scitypes.h | 155 + engines/sci/include/script.h | 214 + engines/sci/include/seg_manager.h | 627 ++ engines/sci/include/sfx_core.h | 40 + engines/sci/include/sfx_engine.h | 179 + engines/sci/include/sfx_iterator.h | 353 + engines/sci/include/sfx_iterator_internal.h | 238 + engines/sci/include/sfx_pcm.h | 235 + engines/sci/include/sfx_player.h | 166 + engines/sci/include/sfx_songlib.h | 200 + engines/sci/include/sfx_time.h | 93 + engines/sci/include/sfx_timer.h | 96 + engines/sci/include/sys_strings.h | 80 + engines/sci/include/uinput.h | 130 + engines/sci/include/util.h | 108 + engines/sci/include/versions.h | 169 + engines/sci/include/vm.h | 843 +++ engines/sci/include/vm_types.h | 71 + engines/sci/include/vocabulary.h | 424 ++ engines/sci/include/win32/Makefile.am | 1 + engines/sci/include/win32/getopt.h | 127 + engines/sci/include/win32/sci_win32.h | 37 + engines/sci/include/win32/usleep.h | 2 + engines/sci/main.c | 1840 +++++ engines/sci/makefile.dos | 20 + engines/sci/menu/Makefile.am | 5 + engines/sci/menu/game_select_init.c | 275 + engines/sci/menu/game_select_screen.c | 581 ++ engines/sci/module.mk | 111 + engines/sci/scicore/Makefile.am | 11 + engines/sci/scicore/aatree.c | 181 + engines/sci/scicore/console.c | 151 + engines/sci/scicore/decompress0.c | 372 + engines/sci/scicore/decompress01.c | 655 ++ engines/sci/scicore/decompress1.c | 443 ++ engines/sci/scicore/decompress11.c | 167 + engines/sci/scicore/exe.c | 86 + engines/sci/scicore/exe.h | 60 + engines/sci/scicore/exe_dec.h | 66 + engines/sci/scicore/exe_lzexe.c | 342 + engines/sci/scicore/exe_raw.c | 73 + engines/sci/scicore/fnmatch.c | 849 +++ engines/sci/scicore/games.h | 125 + engines/sci/scicore/hashmap.c | 186 + engines/sci/scicore/huffmake.pl | 157 + engines/sci/scicore/hufftree.1 | 17 + engines/sci/scicore/hufftree.2 | 69 + engines/sci/scicore/hufftree.3 | 257 + engines/sci/scicore/int_hashmap.c | 33 + engines/sci/scicore/makefile.dos | 19 + engines/sci/scicore/modules.c | 153 + engines/sci/scicore/old_objects.c | 716 ++ engines/sci/scicore/reg_t_hashmap.c | 42 + engines/sci/scicore/resource.c | 951 +++ engines/sci/scicore/resource_map.c | 515 ++ engines/sci/scicore/resource_patch.c | 224 + engines/sci/scicore/resourcecheck.c | 38 + engines/sci/scicore/sci_dos.c | 219 + engines/sci/scicore/sci_memory.c | 311 + engines/sci/scicore/script.c | 488 ++ engines/sci/scicore/tools.c | 780 +++ engines/sci/scicore/treedef.1 | 31 + engines/sci/scicore/treedef.2 | 127 + engines/sci/scicore/treedef.3 | 511 ++ engines/sci/scicore/versions.c | 368 + engines/sci/scicore/vocab.c | 713 ++ engines/sci/scicore/vocab_debug.c | 421 ++ engines/sci/sciv | 15 + engines/sci/scummvm/detection.cpp | 947 +++ engines/sci/scummvm/scummvm.patch | 55 + engines/sci/scummvm/scummvm_engine.cpp | 416 ++ engines/sci/scummvm/scummvm_engine.h | 54 + engines/sci/sfx/Makefile.am | 27 + engines/sci/sfx/adlib.c | 66 + engines/sci/sfx/adlib.h | 69 + engines/sci/sfx/core.c | 938 +++ engines/sci/sfx/device.h | 117 + engines/sci/sfx/device/Makefile.am | 3 + engines/sci/sfx/device/alsa-midi.c | 227 + engines/sci/sfx/device/camd-midi.c | 161 + engines/sci/sfx/device/devices.c | 112 + engines/sci/sfx/device/unixraw-midi.c | 100 + engines/sci/sfx/doc/README | 7 + engines/sci/sfx/doc/patch001.txt | 118 + engines/sci/sfx/doc/sound01.txt | 213 + engines/sci/sfx/iterator.c | 2113 ++++++ engines/sci/sfx/lists/GM.txt | 177 + engines/sci/sfx/lists/gm_patches.c | 198 + engines/sci/sfx/lists/mt32_timbres.c | 181 + engines/sci/sfx/mixer.h | 119 + engines/sci/sfx/mixer/Makefile.am | 6 + engines/sci/sfx/mixer/dc.c | 329 + engines/sci/sfx/mixer/mixers.c | 55 + engines/sci/sfx/mixer/soft.c | 988 +++ engines/sci/sfx/mixer/test.c | 351 + engines/sci/sfx/mt32_GM_mapping/Makefile | 15 + engines/sci/sfx/mt32_GM_mapping/README | 2 + engines/sci/sfx/mt32_GM_mapping/gm_patches.c | 198 + engines/sci/sfx/mt32_GM_mapping/lb2map.txt | 118 + engines/sci/sfx/mt32_GM_mapping/main.c | 157 + engines/sci/sfx/mt32_GM_mapping/mt32_timbres.c | 181 + engines/sci/sfx/mt32_GM_mapping/pq1map.txt | 135 + engines/sci/sfx/mt32_GM_mapping/qfg1map.txt | 123 + engines/sci/sfx/old/Makefile | 24 + engines/sci/sfx/old/README | 6 + engines/sci/sfx/old/ROADMAP | 72 + engines/sci/sfx/old/main.c | 49 + engines/sci/sfx/old/midi.c | 30 + engines/sci/sfx/old/midi.h | 30 + engines/sci/sfx/old/midi_mt32.c | 341 + engines/sci/sfx/old/midi_mt32.h | 29 + engines/sci/sfx/old/midiout.c | 63 + engines/sci/sfx/old/midiout.h | 29 + engines/sci/sfx/old/midiout_alsaraw.c | 51 + engines/sci/sfx/old/midiout_alsaraw.h | 28 + engines/sci/sfx/old/midiout_unixraw.c | 52 + engines/sci/sfx/old/midiout_unixraw.h | 28 + engines/sci/sfx/pcm-iterator.c | 117 + engines/sci/sfx/pcm_device/Makefile.am | 7 + engines/sci/sfx/pcm_device/alsa.c | 387 ++ engines/sci/sfx/pcm_device/audbuf_test.c | 190 + engines/sci/sfx/pcm_device/audiobuf.c | 348 + engines/sci/sfx/pcm_device/audiobuf.h | 140 + engines/sci/sfx/pcm_device/pcm_devices.c | 73 + engines/sci/sfx/pcm_device/scummvm.cpp | 60 + engines/sci/sfx/pcm_device/sdl.c | 272 + engines/sci/sfx/player/Makefile.am | 3 + engines/sci/sfx/player/players.c | 54 + engines/sci/sfx/player/polled.c | 335 + engines/sci/sfx/player/realtime.c | 328 + engines/sci/sfx/seq/Makefile.am | 5 + engines/sci/sfx/seq/gm.c | 185 + engines/sci/sfx/seq/instrument-map.c | 539 ++ engines/sci/sfx/seq/instrument-map.h | 132 + engines/sci/sfx/seq/map-mt32-to-gm.c | 813 +++ engines/sci/sfx/seq/mt32.c | 480 ++ engines/sci/sfx/seq/oss-adlib.c | 374 + engines/sci/sfx/seq/sequencers.c | 68 + engines/sci/sfx/sequencer.h | 142 + engines/sci/sfx/softseq.h | 134 + engines/sci/sfx/softseq/Makefile.am | 5 + engines/sci/sfx/softseq/SN76496.c | 248 + engines/sci/sfx/softseq/amiga.c | 658 ++ engines/sci/sfx/softseq/fluidsynth.c | 262 + engines/sci/sfx/softseq/fmopl.c | 1144 +++ engines/sci/sfx/softseq/fmopl.h | 168 + engines/sci/sfx/softseq/mt32.cpp | 228 + engines/sci/sfx/softseq/mt32/Makefile.am | 5 + engines/sci/sfx/softseq/mt32/freeverb.cpp | 310 + engines/sci/sfx/softseq/mt32/freeverb.h | 239 + engines/sci/sfx/softseq/mt32/i386.cpp | 849 +++ engines/sci/sfx/softseq/mt32/i386.h | 49 + engines/sci/sfx/softseq/mt32/mt32_file.cpp | 112 + engines/sci/sfx/softseq/mt32/mt32_file.h | 67 + engines/sci/sfx/softseq/mt32/mt32emu.h | 70 + engines/sci/sfx/softseq/mt32/part.cpp | 632 ++ engines/sci/sfx/softseq/mt32/part.h | 113 + engines/sci/sfx/softseq/mt32/partial.cpp | 960 +++ engines/sci/sfx/softseq/mt32/partial.h | 148 + engines/sci/sfx/softseq/mt32/partialManager.cpp | 272 + engines/sci/sfx/softseq/mt32/partialManager.h | 56 + engines/sci/sfx/softseq/mt32/structures.h | 284 + engines/sci/sfx/softseq/mt32/synth.cpp | 1199 ++++ engines/sci/sfx/softseq/mt32/synth.h | 300 + engines/sci/sfx/softseq/mt32/tables.cpp | 749 ++ engines/sci/sfx/softseq/mt32/tables.h | 116 + engines/sci/sfx/softseq/opl2.c | 718 ++ engines/sci/sfx/softseq/pcspeaker.c | 184 + engines/sci/sfx/softseq/softsequencers.c | 71 + engines/sci/sfx/songlib.c | 282 + engines/sci/sfx/test-iterator.c | 450 ++ engines/sci/sfx/tests/stubs.c | 9 + engines/sci/sfx/tests/tests.cpp | 172 + engines/sci/sfx/tests/tests.vcproj | 158 + engines/sci/sfx/time.c | 131 + engines/sci/sfx/timer/Makefile.am | 4 + engines/sci/sfx/timer/pthread.c | 104 + engines/sci/sfx/timer/scummvm.cpp | 52 + engines/sci/sfx/timer/sigalrm.c | 157 + engines/sci/sfx/timer/timers.c | 73 + engines/sci/sfx/timetest.c | 59 + engines/sci/tools/5x8.font | 2307 +++++++ engines/sci/tools/6x10.font | 2819 ++++++++ engines/sci/tools/Makefile.am | 19 + engines/sci/tools/bdf.c | 7252 ++++++++++++++++++++ engines/sci/tools/bdf.h | 675 ++ engines/sci/tools/bdfP.h | 142 + engines/sci/tools/bdfgname.c | 431 ++ engines/sci/tools/bdfgrid.c | 3361 +++++++++ engines/sci/tools/bdftofont.c | 181 + engines/sci/tools/classes.c | 54 + engines/sci/tools/fonttoc.c | 263 + engines/sci/tools/graphics_png.h | 113 + engines/sci/tools/listwords.c | 91 + engines/sci/tools/makefile.dos | 38 + engines/sci/tools/musicplayer.c | 115 + engines/sci/tools/scidisasm.c | 1009 +++ engines/sci/tools/scipack.c | 217 + engines/sci/tools/sciunpack.c | 482 ++ engines/sci/tools/sciunpack.h | 70 + engines/sci/tools/scriptdump.c | 49 + engines/sci/tools/vocabdump.c | 77 + engines/sci/win32/Makefile.am | 6 + engines/sci/win32/freesci.def | 103 + engines/sci/win32/getopt.c | 755 ++ engines/sci/win32/msvc71-2003/freesci.sln | 34 + .../sci/win32/msvc71-2003/fsci_dll/fsci_dll.vcproj | 932 +++ engines/sci/win32/msvc71-2003/sciv/sciv.vcproj | 187 + engines/sci/win32/msvc80-2005/freesci.sln | 28 + .../sci/win32/msvc80-2005/fsci_dll/fsci_dll.vcproj | 1218 ++++ engines/sci/win32/msvc80-2005/sciv/sciv.vcproj | 260 + engines/sci/win32/msvc90-2008/freesci.sln | 28 + .../sci/win32/msvc90-2008/fsci_dll/fsci_dll.vcproj | 1223 ++++ engines/sci/win32/msvc90-2008/sciv/sciv.vcproj | 262 + engines/sci/win32/usleep.c | 25 + engines/sci/yywrap.c | 4 + 385 files changed, 163544 insertions(+) create mode 100644 engines/sci/Makefile.am create mode 100644 engines/sci/README create mode 100644 engines/sci/config.c create mode 100644 engines/sci/config.l create mode 100644 engines/sci/config/Makefile.am create mode 100644 engines/sci/config/config.l create mode 100644 engines/sci/config/config.test create mode 100644 engines/sci/config/dynlink.c create mode 100644 engines/sci/config/extension.c create mode 100644 engines/sci/config/lsl2.scifx create mode 100644 engines/sci/config/option-test.conf create mode 100644 engines/sci/config/sq3.scifx create mode 100644 engines/sci/config/test-options.c create mode 100644 engines/sci/config/test-parse.c create mode 100644 engines/sci/dc/3dutils.c create mode 100644 engines/sci/dc/Makefile create mode 100644 engines/sci/dc/aica.h create mode 100644 engines/sci/dc/aica_cmd_iface.h create mode 100644 engines/sci/dc/aica_crt0.s create mode 100644 engines/sci/dc/aica_main.c create mode 100644 engines/sci/dc/aica_sup.c create mode 100644 engines/sci/dc/bkg.c create mode 100644 engines/sci/dc/config.h.in create mode 100644 engines/sci/dc/dc.h create mode 100644 engines/sci/dc/dc_save.c create mode 100644 engines/sci/dc/gamemenu.c create mode 100644 engines/sci/dc/gp.h create mode 100644 engines/sci/dc/keyboard.c create mode 100644 engines/sci/dc/keyboard.h create mode 100644 engines/sci/dc/mouse1.h create mode 100644 engines/sci/dc/options.h create mode 100644 engines/sci/dc/selectgame.c create mode 100644 engines/sci/dc/selectgame.h create mode 100644 engines/sci/dc/sintab.h create mode 100644 engines/sci/dc/snd_stream.c create mode 100644 engines/sci/dc/stream.h create mode 100644 engines/sci/dc/texture.c create mode 100644 engines/sci/engine/Makefile.am create mode 100644 engines/sci/engine/cfsml.pl create mode 100644 engines/sci/engine/game.c create mode 100644 engines/sci/engine/gc.c create mode 100644 engines/sci/engine/gc.h create mode 100644 engines/sci/engine/grammar.c create mode 100644 engines/sci/engine/heap.c create mode 100644 engines/sci/engine/heap.h create mode 100644 engines/sci/engine/kemu_old.c create mode 100644 engines/sci/engine/kernel.c create mode 100644 engines/sci/engine/kernel_compat.h create mode 100644 engines/sci/engine/kernel_types.h create mode 100644 engines/sci/engine/kevent.c create mode 100644 engines/sci/engine/kfile.c create mode 100644 engines/sci/engine/kgraphics.c create mode 100644 engines/sci/engine/klists.c create mode 100644 engines/sci/engine/kmath.c create mode 100644 engines/sci/engine/kmenu.c create mode 100644 engines/sci/engine/kmovement.c create mode 100644 engines/sci/engine/kpathing.c create mode 100644 engines/sci/engine/kscripts.c create mode 100644 engines/sci/engine/ksound.c create mode 100644 engines/sci/engine/kstring.c create mode 100644 engines/sci/engine/makefile.dos create mode 100644 engines/sci/engine/message.c create mode 100644 engines/sci/engine/message.h create mode 100644 engines/sci/engine/said.c create mode 100644 engines/sci/engine/said.y create mode 100644 engines/sci/engine/savegame.c create mode 100644 engines/sci/engine/savegame.cfsml create mode 100644 engines/sci/engine/sci_graphics.h create mode 100644 engines/sci/engine/scriptconsole.c create mode 100644 engines/sci/engine/scriptdebug.c create mode 100644 engines/sci/engine/seg_manager.c create mode 100644 engines/sci/engine/simplesaid.c create mode 100644 engines/sci/engine/sys_strings.c create mode 100644 engines/sci/engine/vm.c create mode 100644 engines/sci/gfx/Makefile.am create mode 100644 engines/sci/gfx/alpha_mvi_crossblit.c create mode 100644 engines/sci/gfx/antialias.c create mode 100644 engines/sci/gfx/drivers/EXPORTS_ggi create mode 100644 engines/sci/gfx/drivers/EXPORTS_sdl create mode 100644 engines/sci/gfx/drivers/EXPORTS_xlib create mode 100644 engines/sci/gfx/drivers/Makefile.am create mode 100644 engines/sci/gfx/drivers/dc_driver.c create mode 100644 engines/sci/gfx/drivers/dd_driver.cpp create mode 100644 engines/sci/gfx/drivers/dd_driver.h create mode 100644 engines/sci/gfx/drivers/dd_driver_line.cpp create mode 100644 engines/sci/gfx/drivers/directfb_driver.c create mode 100644 engines/sci/gfx/drivers/dx_driver.cpp create mode 100644 engines/sci/gfx/drivers/dx_driver.h create mode 100644 engines/sci/gfx/drivers/gfx_drivers.c create mode 100644 engines/sci/gfx/drivers/ggi_driver.c create mode 100644 engines/sci/gfx/drivers/null_driver.c create mode 100644 engines/sci/gfx/drivers/scummvm_driver.cpp create mode 100644 engines/sci/gfx/drivers/sdl_driver.c create mode 100644 engines/sci/gfx/drivers/xlib_driver.c create mode 100644 engines/sci/gfx/font-5x8.c create mode 100644 engines/sci/gfx/font-6x10.c create mode 100644 engines/sci/gfx/font.c create mode 100644 engines/sci/gfx/gfx_console.c create mode 100644 engines/sci/gfx/gfx_crossblit.c create mode 100644 engines/sci/gfx/gfx_line.c create mode 100644 engines/sci/gfx/gfx_pixmap_scale.c create mode 100644 engines/sci/gfx/gfx_res_options.c create mode 100644 engines/sci/gfx/gfx_resource.c create mode 100644 engines/sci/gfx/gfx_support.c create mode 100644 engines/sci/gfx/gfx_test.c create mode 100644 engines/sci/gfx/gfx_tools.c create mode 100644 engines/sci/gfx/menubar.c create mode 100644 engines/sci/gfx/operations.c create mode 100644 engines/sci/gfx/resmgr.c create mode 100644 engines/sci/gfx/resource/Makefile.am create mode 100644 engines/sci/gfx/resource/sci_cursor_0.c create mode 100644 engines/sci/gfx/resource/sci_font.c create mode 100644 engines/sci/gfx/resource/sci_pal_1.c create mode 100644 engines/sci/gfx/resource/sci_pic_0.c create mode 100644 engines/sci/gfx/resource/sci_picfill.c create mode 100644 engines/sci/gfx/resource/sci_picfill_aux.c create mode 100644 engines/sci/gfx/resource/sci_resmgr.c create mode 100644 engines/sci/gfx/resource/sci_view_0.c create mode 100644 engines/sci/gfx/resource/sci_view_1.c create mode 100644 engines/sci/gfx/sbtree.c create mode 100644 engines/sci/gfx/sci_widgets.c create mode 100644 engines/sci/gfx/widgets.c create mode 100644 engines/sci/gfx/wrapper.c create mode 100644 engines/sci/include/Makefile.am create mode 100644 engines/sci/include/aatree.h create mode 100644 engines/sci/include/beos/Makefile.am create mode 100644 engines/sci/include/beos/collsyms.h create mode 100644 engines/sci/include/beos/fnmatch.h create mode 100644 engines/sci/include/conf_driver.h create mode 100644 engines/sci/include/conf_extension.h create mode 100644 engines/sci/include/conf_parse.h create mode 100644 engines/sci/include/conf_subsystems.h create mode 100644 engines/sci/include/conf_summary.h create mode 100644 engines/sci/include/console.h create mode 100644 engines/sci/include/engine.h create mode 100644 engines/sci/include/event.h create mode 100644 engines/sci/include/game_select.h create mode 100644 engines/sci/include/gfx_driver.h create mode 100644 engines/sci/include/gfx_operations.h create mode 100644 engines/sci/include/gfx_options.h create mode 100644 engines/sci/include/gfx_res_options.h create mode 100644 engines/sci/include/gfx_resmgr.h create mode 100644 engines/sci/include/gfx_resource.h create mode 100644 engines/sci/include/gfx_state_internal.h create mode 100644 engines/sci/include/gfx_system.h create mode 100644 engines/sci/include/gfx_tools.h create mode 100644 engines/sci/include/gfx_widgets.h create mode 100644 engines/sci/include/hashmap.h create mode 100644 engines/sci/include/heapmgr.h create mode 100644 engines/sci/include/int_hashmap.h create mode 100644 engines/sci/include/kdebug.h create mode 100644 engines/sci/include/kernel.h create mode 100644 engines/sci/include/list.h create mode 100644 engines/sci/include/listener.h create mode 100644 engines/sci/include/menubar.h create mode 100644 engines/sci/include/modules.h create mode 100644 engines/sci/include/old_objects.h create mode 100644 engines/sci/include/reg_t_hashmap.h create mode 100644 engines/sci/include/resource.h create mode 100644 engines/sci/include/sbtree.h create mode 100644 engines/sci/include/sci_conf.h create mode 100644 engines/sci/include/sci_dos.h create mode 100644 engines/sci/include/sci_memory.h create mode 100644 engines/sci/include/sci_midi.h create mode 100644 engines/sci/include/sci_widgets.h create mode 100644 engines/sci/include/sciresource.h create mode 100644 engines/sci/include/scitypes.h create mode 100644 engines/sci/include/script.h create mode 100644 engines/sci/include/seg_manager.h create mode 100644 engines/sci/include/sfx_core.h create mode 100644 engines/sci/include/sfx_engine.h create mode 100644 engines/sci/include/sfx_iterator.h create mode 100644 engines/sci/include/sfx_iterator_internal.h create mode 100644 engines/sci/include/sfx_pcm.h create mode 100644 engines/sci/include/sfx_player.h create mode 100644 engines/sci/include/sfx_songlib.h create mode 100644 engines/sci/include/sfx_time.h create mode 100644 engines/sci/include/sfx_timer.h create mode 100644 engines/sci/include/sys_strings.h create mode 100644 engines/sci/include/uinput.h create mode 100644 engines/sci/include/util.h create mode 100644 engines/sci/include/versions.h create mode 100644 engines/sci/include/vm.h create mode 100644 engines/sci/include/vm_types.h create mode 100644 engines/sci/include/vocabulary.h create mode 100644 engines/sci/include/win32/Makefile.am create mode 100644 engines/sci/include/win32/getopt.h create mode 100644 engines/sci/include/win32/sci_win32.h create mode 100644 engines/sci/include/win32/usleep.h create mode 100644 engines/sci/main.c create mode 100644 engines/sci/makefile.dos create mode 100644 engines/sci/menu/Makefile.am create mode 100644 engines/sci/menu/game_select_init.c create mode 100644 engines/sci/menu/game_select_screen.c create mode 100644 engines/sci/module.mk create mode 100644 engines/sci/scicore/Makefile.am create mode 100644 engines/sci/scicore/aatree.c create mode 100644 engines/sci/scicore/console.c create mode 100644 engines/sci/scicore/decompress0.c create mode 100644 engines/sci/scicore/decompress01.c create mode 100644 engines/sci/scicore/decompress1.c create mode 100644 engines/sci/scicore/decompress11.c create mode 100644 engines/sci/scicore/exe.c create mode 100644 engines/sci/scicore/exe.h create mode 100644 engines/sci/scicore/exe_dec.h create mode 100644 engines/sci/scicore/exe_lzexe.c create mode 100644 engines/sci/scicore/exe_raw.c create mode 100644 engines/sci/scicore/fnmatch.c create mode 100644 engines/sci/scicore/games.h create mode 100644 engines/sci/scicore/hashmap.c create mode 100644 engines/sci/scicore/huffmake.pl create mode 100644 engines/sci/scicore/hufftree.1 create mode 100644 engines/sci/scicore/hufftree.2 create mode 100644 engines/sci/scicore/hufftree.3 create mode 100644 engines/sci/scicore/int_hashmap.c create mode 100644 engines/sci/scicore/makefile.dos create mode 100644 engines/sci/scicore/modules.c create mode 100644 engines/sci/scicore/old_objects.c create mode 100644 engines/sci/scicore/reg_t_hashmap.c create mode 100644 engines/sci/scicore/resource.c create mode 100644 engines/sci/scicore/resource_map.c create mode 100644 engines/sci/scicore/resource_patch.c create mode 100644 engines/sci/scicore/resourcecheck.c create mode 100644 engines/sci/scicore/sci_dos.c create mode 100644 engines/sci/scicore/sci_memory.c create mode 100644 engines/sci/scicore/script.c create mode 100644 engines/sci/scicore/tools.c create mode 100644 engines/sci/scicore/treedef.1 create mode 100644 engines/sci/scicore/treedef.2 create mode 100644 engines/sci/scicore/treedef.3 create mode 100644 engines/sci/scicore/versions.c create mode 100644 engines/sci/scicore/vocab.c create mode 100644 engines/sci/scicore/vocab_debug.c create mode 100644 engines/sci/sciv create mode 100644 engines/sci/scummvm/detection.cpp create mode 100644 engines/sci/scummvm/scummvm.patch create mode 100644 engines/sci/scummvm/scummvm_engine.cpp create mode 100644 engines/sci/scummvm/scummvm_engine.h create mode 100644 engines/sci/sfx/Makefile.am create mode 100644 engines/sci/sfx/adlib.c create mode 100644 engines/sci/sfx/adlib.h create mode 100644 engines/sci/sfx/core.c create mode 100644 engines/sci/sfx/device.h create mode 100644 engines/sci/sfx/device/Makefile.am create mode 100644 engines/sci/sfx/device/alsa-midi.c create mode 100644 engines/sci/sfx/device/camd-midi.c create mode 100644 engines/sci/sfx/device/devices.c create mode 100644 engines/sci/sfx/device/unixraw-midi.c create mode 100644 engines/sci/sfx/doc/README create mode 100644 engines/sci/sfx/doc/patch001.txt create mode 100644 engines/sci/sfx/doc/sound01.txt create mode 100644 engines/sci/sfx/iterator.c create mode 100644 engines/sci/sfx/lists/GM.txt create mode 100644 engines/sci/sfx/lists/gm_patches.c create mode 100644 engines/sci/sfx/lists/mt32_timbres.c create mode 100644 engines/sci/sfx/mixer.h create mode 100644 engines/sci/sfx/mixer/Makefile.am create mode 100644 engines/sci/sfx/mixer/dc.c create mode 100644 engines/sci/sfx/mixer/mixers.c create mode 100644 engines/sci/sfx/mixer/soft.c create mode 100644 engines/sci/sfx/mixer/test.c create mode 100644 engines/sci/sfx/mt32_GM_mapping/Makefile create mode 100644 engines/sci/sfx/mt32_GM_mapping/README create mode 100644 engines/sci/sfx/mt32_GM_mapping/gm_patches.c create mode 100644 engines/sci/sfx/mt32_GM_mapping/lb2map.txt create mode 100644 engines/sci/sfx/mt32_GM_mapping/main.c create mode 100644 engines/sci/sfx/mt32_GM_mapping/mt32_timbres.c create mode 100644 engines/sci/sfx/mt32_GM_mapping/pq1map.txt create mode 100644 engines/sci/sfx/mt32_GM_mapping/qfg1map.txt create mode 100644 engines/sci/sfx/old/Makefile create mode 100644 engines/sci/sfx/old/README create mode 100644 engines/sci/sfx/old/ROADMAP create mode 100644 engines/sci/sfx/old/main.c create mode 100644 engines/sci/sfx/old/midi.c create mode 100644 engines/sci/sfx/old/midi.h create mode 100644 engines/sci/sfx/old/midi_mt32.c create mode 100644 engines/sci/sfx/old/midi_mt32.h create mode 100644 engines/sci/sfx/old/midiout.c create mode 100644 engines/sci/sfx/old/midiout.h create mode 100644 engines/sci/sfx/old/midiout_alsaraw.c create mode 100644 engines/sci/sfx/old/midiout_alsaraw.h create mode 100644 engines/sci/sfx/old/midiout_unixraw.c create mode 100644 engines/sci/sfx/old/midiout_unixraw.h create mode 100644 engines/sci/sfx/pcm-iterator.c create mode 100644 engines/sci/sfx/pcm_device/Makefile.am create mode 100644 engines/sci/sfx/pcm_device/alsa.c create mode 100644 engines/sci/sfx/pcm_device/audbuf_test.c create mode 100644 engines/sci/sfx/pcm_device/audiobuf.c create mode 100644 engines/sci/sfx/pcm_device/audiobuf.h create mode 100644 engines/sci/sfx/pcm_device/pcm_devices.c create mode 100644 engines/sci/sfx/pcm_device/scummvm.cpp create mode 100644 engines/sci/sfx/pcm_device/sdl.c create mode 100644 engines/sci/sfx/player/Makefile.am create mode 100644 engines/sci/sfx/player/players.c create mode 100644 engines/sci/sfx/player/polled.c create mode 100644 engines/sci/sfx/player/realtime.c create mode 100644 engines/sci/sfx/seq/Makefile.am create mode 100644 engines/sci/sfx/seq/gm.c create mode 100644 engines/sci/sfx/seq/instrument-map.c create mode 100644 engines/sci/sfx/seq/instrument-map.h create mode 100644 engines/sci/sfx/seq/map-mt32-to-gm.c create mode 100644 engines/sci/sfx/seq/mt32.c create mode 100644 engines/sci/sfx/seq/oss-adlib.c create mode 100644 engines/sci/sfx/seq/sequencers.c create mode 100644 engines/sci/sfx/sequencer.h create mode 100644 engines/sci/sfx/softseq.h create mode 100644 engines/sci/sfx/softseq/Makefile.am create mode 100644 engines/sci/sfx/softseq/SN76496.c create mode 100644 engines/sci/sfx/softseq/amiga.c create mode 100644 engines/sci/sfx/softseq/fluidsynth.c create mode 100644 engines/sci/sfx/softseq/fmopl.c create mode 100644 engines/sci/sfx/softseq/fmopl.h create mode 100644 engines/sci/sfx/softseq/mt32.cpp create mode 100644 engines/sci/sfx/softseq/mt32/Makefile.am create mode 100644 engines/sci/sfx/softseq/mt32/freeverb.cpp create mode 100644 engines/sci/sfx/softseq/mt32/freeverb.h create mode 100644 engines/sci/sfx/softseq/mt32/i386.cpp create mode 100644 engines/sci/sfx/softseq/mt32/i386.h create mode 100644 engines/sci/sfx/softseq/mt32/mt32_file.cpp create mode 100644 engines/sci/sfx/softseq/mt32/mt32_file.h create mode 100644 engines/sci/sfx/softseq/mt32/mt32emu.h create mode 100644 engines/sci/sfx/softseq/mt32/part.cpp create mode 100644 engines/sci/sfx/softseq/mt32/part.h create mode 100644 engines/sci/sfx/softseq/mt32/partial.cpp create mode 100644 engines/sci/sfx/softseq/mt32/partial.h create mode 100644 engines/sci/sfx/softseq/mt32/partialManager.cpp create mode 100644 engines/sci/sfx/softseq/mt32/partialManager.h create mode 100644 engines/sci/sfx/softseq/mt32/structures.h create mode 100644 engines/sci/sfx/softseq/mt32/synth.cpp create mode 100644 engines/sci/sfx/softseq/mt32/synth.h create mode 100644 engines/sci/sfx/softseq/mt32/tables.cpp create mode 100644 engines/sci/sfx/softseq/mt32/tables.h create mode 100644 engines/sci/sfx/softseq/opl2.c create mode 100644 engines/sci/sfx/softseq/pcspeaker.c create mode 100644 engines/sci/sfx/softseq/softsequencers.c create mode 100644 engines/sci/sfx/songlib.c create mode 100644 engines/sci/sfx/test-iterator.c create mode 100644 engines/sci/sfx/tests/stubs.c create mode 100644 engines/sci/sfx/tests/tests.cpp create mode 100644 engines/sci/sfx/tests/tests.vcproj create mode 100644 engines/sci/sfx/time.c create mode 100644 engines/sci/sfx/timer/Makefile.am create mode 100644 engines/sci/sfx/timer/pthread.c create mode 100644 engines/sci/sfx/timer/scummvm.cpp create mode 100644 engines/sci/sfx/timer/sigalrm.c create mode 100644 engines/sci/sfx/timer/timers.c create mode 100644 engines/sci/sfx/timetest.c create mode 100644 engines/sci/tools/5x8.font create mode 100644 engines/sci/tools/6x10.font create mode 100644 engines/sci/tools/Makefile.am create mode 100644 engines/sci/tools/bdf.c create mode 100644 engines/sci/tools/bdf.h create mode 100644 engines/sci/tools/bdfP.h create mode 100644 engines/sci/tools/bdfgname.c create mode 100644 engines/sci/tools/bdfgrid.c create mode 100644 engines/sci/tools/bdftofont.c create mode 100644 engines/sci/tools/classes.c create mode 100644 engines/sci/tools/fonttoc.c create mode 100644 engines/sci/tools/graphics_png.h create mode 100644 engines/sci/tools/listwords.c create mode 100644 engines/sci/tools/makefile.dos create mode 100644 engines/sci/tools/musicplayer.c create mode 100644 engines/sci/tools/scidisasm.c create mode 100644 engines/sci/tools/scipack.c create mode 100644 engines/sci/tools/sciunpack.c create mode 100644 engines/sci/tools/sciunpack.h create mode 100644 engines/sci/tools/scriptdump.c create mode 100644 engines/sci/tools/vocabdump.c create mode 100644 engines/sci/win32/Makefile.am create mode 100644 engines/sci/win32/freesci.def create mode 100644 engines/sci/win32/getopt.c create mode 100644 engines/sci/win32/msvc71-2003/freesci.sln create mode 100644 engines/sci/win32/msvc71-2003/fsci_dll/fsci_dll.vcproj create mode 100644 engines/sci/win32/msvc71-2003/sciv/sciv.vcproj create mode 100644 engines/sci/win32/msvc80-2005/freesci.sln create mode 100644 engines/sci/win32/msvc80-2005/fsci_dll/fsci_dll.vcproj create mode 100644 engines/sci/win32/msvc80-2005/sciv/sciv.vcproj create mode 100644 engines/sci/win32/msvc90-2008/freesci.sln create mode 100644 engines/sci/win32/msvc90-2008/fsci_dll/fsci_dll.vcproj create mode 100644 engines/sci/win32/msvc90-2008/sciv/sciv.vcproj create mode 100644 engines/sci/win32/usleep.c create mode 100644 engines/sci/yywrap.c diff --git a/base/plugins.cpp b/base/plugins.cpp index 2efab3fba4..446c26e57c 100644 --- a/base/plugins.cpp +++ b/base/plugins.cpp @@ -136,6 +136,9 @@ public: #if PLUGIN_ENABLED_STATIC(SAGA) LINK_PLUGIN(SAGA) #endif + #if PLUGIN_ENABLED_STATIC(SCI) + LINK_PLUGIN(SCI) + #endif #if PLUGIN_ENABLED_STATIC(SKY) LINK_PLUGIN(SKY) #endif diff --git a/configure b/configure index 0b952d82ac..87e750ef8d 100755 --- a/configure +++ b/configure @@ -103,6 +103,7 @@ add_engine queen "Flight of the Amazon Queen" yes add_engine saga "SAGA" yes "ihnm saga2" add_engine ihnm "IHNM" yes add_engine saga2 "SAGA 2 games" no +add_engine sci "SCI" no add_engine sky "Beneath a Steel Sky" yes add_engine sword1 "Broken Sword 1" yes add_engine sword2 "Broken Sword 2" yes diff --git a/engines/engines.mk b/engines/engines.mk index 7a4637b757..fa97631785 100644 --- a/engines/engines.mk +++ b/engines/engines.mk @@ -96,6 +96,11 @@ endif endif +ifdef ENABLE_SCI +DEFINES += -DENABLE_SCI=$(ENABLE_SCI) +MODULES += engines/sci +endif + ifdef ENABLE_SKY DEFINES += -DENABLE_SKY=$(ENABLE_SKY) MODULES += engines/sky diff --git a/engines/sci/Makefile.am b/engines/sci/Makefile.am new file mode 100644 index 0000000000..ad50a7e20e --- /dev/null +++ b/engines/sci/Makefile.am @@ -0,0 +1,24 @@ +SUBDIRS = scicore engine include gfx sfx tools menu win32 +EXTRA_DIST = README yywrap.c config.l sciv dc/3dutils.c dc/Makefile \ + dc/bkg.c dc/config.h.in dc/dc.h dc/dc_save.c dc/gamemenu.c dc/gp.h \ + dc/keyboard.c dc/keyboard.h dc/mouse1.h dc/options.h \ + dc/selectgame.c dc/selectgame.h dc/sintab.h dc/texture.c dc/aica.h \ + dc/aica_cmd_iface.h dc/aica_crt0.s dc/aica_main.c dc/aica_sup.c \ + dc/snd_stream.c dc/stream.h +INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@ +AM_CFLAGS = $(SDL_CFLAGS) +LDADD = engine/libsciengine.a gfx/libscigraphics.a \ + gfx/resource/libsciresources.a gfx/drivers/libscidrivers.a \ + sfx/libscisound.a scicore/libscicore.a \ + sfx/player/libsciplayer.a \ + sfx/seq/libsciseq.a sfx/timer/libscitimer.a \ + sfx/pcm_device/libscipcm.a sfx/mixer/libscimixer.a \ + sfx/softseq/libscisoftseq.a \ + sfx/libscisoundlib.a \ + sfx/device/libscisounddevice.a \ + win32/libsciwin32.a \ + menu/libscimenu.a \ + @ac_readline@ @ac_curses_libraries@ @ac_png_libraries@ @LIB_M@ @SDL_LIBS@ +bin_PROGRAMS = freesci +freesci_SOURCES = config.l main.c +freesci_LDFLAGS = @SCIV_LDFLAGS@ diff --git a/engines/sci/README b/engines/sci/README new file mode 100644 index 0000000000..e7710d0565 --- /dev/null +++ b/engines/sci/README @@ -0,0 +1,19 @@ +Directory structure: + +scicore/ contains all files concerning resource management and (de)compression, + +engine/ contains the files directly related to the VM and the text parser, such +as heap and state management and handling for script and vocab resources. + +graphics/ contains graphics and input management (since input handlers are +usually bundled with graphics libraries) and stuff for specific graphics/ +input libs. + +include/ contains all global header files. + +sound/ contains sound management, both general and specific to certain +libraries/interfaces/daemons. + +tools/ contains all 'tools' for working with SCI resource data. + +win32/ contains the workspace and projects for Visual C++. diff --git a/engines/sci/config.c b/engines/sci/config.c new file mode 100644 index 0000000000..35c0a4596c --- /dev/null +++ b/engines/sci/config.c @@ -0,0 +1,3439 @@ + +#line 3 "config.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 33 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart(yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +extern int yyleng; + +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef unsigned int yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart (FILE *input_file ); +void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ); +void yy_delete_buffer (YY_BUFFER_STATE b ); +void yy_flush_buffer (YY_BUFFER_STATE b ); +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ); +void yypop_buffer_state (void ); + +static void yyensure_buffer_stack (void ); +static void yy_load_buffer_state (void ); +static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); + +void *yyalloc (yy_size_t ); +void *yyrealloc (void *,yy_size_t ); +void yyfree (void * ); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +typedef unsigned char YY_CHAR; + +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; + +typedef int yy_state_type; + +extern int yylineno; + +int yylineno = 1; + +extern char *yytext; +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + yyleng = (size_t) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 13 +#define YY_END_OF_BUFFER 14 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[315] = + { 0, + 12, 12, 14, 12, 11, 11, 12, 12, 12, 12, + 6, 12, 12, 12, 12, 12, 12, 12, 11, 11, + 6, 12, 12, 12, 12, 12, 12, 6, 6, 6, + 6, 6, 6, 12, 12, 12, 12, 12, 12, 12, + 10, 12, 12, 12, 6, 6, 6, 6, 6, 6, + 6, 1, 12, 12, 12, 12, 12, 12, 12, 12, + 6, 6, 6, 0, 12, 12, 6, 6, 12, 12, + 12, 12, 0, 12, 12, 12, 6, 12, 12, 12, + 12, 12, 0, 6, 0, 12, 12, 6, 12, 12, + 0, 12, 0, 12, 8, 6, 6, 6, 6, 12, + + 12, 12, 12, 5, 12, 0, 12, 12, 0, 0, + 6, 0, 12, 12, 6, 12, 0, 8, 12, 0, + 12, 12, 8, 6, 12, 12, 12, 5, 5, 5, + 5, 5, 12, 0, 12, 0, 12, 12, 0, 0, + 6, 12, 0, 0, 0, 8, 12, 12, 12, 12, + 5, 5, 5, 5, 5, 6, 5, 12, 0, 12, + 12, 0, 0, 6, 7, 12, 12, 12, 6, 12, + 12, 5, 5, 5, 12, 12, 5, 6, 5, 12, + 0, 12, 12, 6, 6, 12, 9, 12, 5, 12, + 12, 5, 12, 12, 6, 12, 12, 12, 5, 12, + + 12, 5, 12, 6, 12, 6, 12, 12, 12, 12, + 5, 4, 4, 4, 4, 4, 4, 4, 12, 6, + 12, 12, 12, 5, 4, 4, 4, 4, 4, 4, + 4, 4, 12, 6, 6, 12, 4, 4, 4, 4, + 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, + 6, 4, 4, 4, 4, 4, 3, 3, 3, 3, + 3, 3, 3, 3, 6, 4, 4, 4, 4, 4, + 4, 4, 3, 3, 3, 3, 3, 3, 3, 6, + 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, + 2, 4, 4, 4, 4, 3, 3, 3, 3, 3, + + 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 0 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 5, 6, 1, 7, 1, 1, 8, + 1, 1, 1, 9, 10, 11, 12, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 10, 14, 15, + 16, 17, 1, 1, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 19, 20, 21, 1, 22, 1, 18, 23, 24, 25, + + 26, 18, 27, 18, 28, 18, 18, 29, 30, 31, + 32, 33, 18, 34, 35, 18, 36, 37, 38, 18, + 18, 18, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst flex_int32_t yy_meta[39] = + { 0, + 1, 1, 2, 1, 3, 1, 1, 1, 1, 1, + 1, 1, 4, 1, 1, 1, 1, 4, 1, 1, + 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4 + } ; + +static yyconst flex_int16_t yy_base[337] = + { 0, + 0, 0, 966, 0, 37, 39, 53, 0, 932, 74, + 112, 150, 918, 922, 915, 916, 17, 0, 47, 44, + 0, 42, 188, 929, 900, 197, 0, 0, 235, 273, + 293, 46, 306, 909, 886, 896, 894, 877, 877, 884, + 2229, 878, 885, 890, 196, 876, 344, 874, 381, 394, + 843, 0, 804, 793, 432, 791, 783, 779, 778, 453, + 209, 0, 384, 490, 493, 497, 505, 0, 769, 764, + 518, 50, 782, 537, 551, 560, 598, 753, 755, 746, + 197, 618, 626, 543, 630, 642, 646, 654, 741, 752, + 50, 659, 270, 663, 0, 0, 677, 510, 698, 688, + + 684, 686, 281, 719, 756, 760, 763, 775, 768, 783, + 795, 791, 807, 811, 823, 673, 816, 2229, 828, 272, + 692, 832, 687, 674, 648, 639, 628, 0, 846, 884, + 669, 214, 904, 912, 924, 920, 936, 948, 932, 960, + 640, 610, 941, 633, 953, 623, 577, 283, 566, 575, + 298, 536, 973, 498, 1010, 1023, 503, 1013, 1060, 1063, + 1075, 1068, 1087, 299, 2229, 479, 434, 289, 204, 435, + 376, 511, 0, 1099, 1103, 1115, 1120, 539, 390, 432, + 564, 365, 322, 562, 838, 292, 0, 1132, 1144, 1148, + 1160, 1165, 515, 274, 1080, 1177, 1181, 1193, 1205, 1209, + + 1221, 1233, 534, 1246, 262, 1081, 1283, 1226, 1295, 1307, + 225, 0, 0, 1320, 1358, 1108, 574, 1379, 609, 1125, + 1300, 1416, 1428, 635, 213, 636, 199, 1441, 187, 1478, + 1491, 191, 659, 1529, 1423, 569, 800, 0, 1481, 1566, + 1570, 1578, 0, 0, 0, 1591, 1629, 1153, 801, 1650, + 1186, 1687, 1699, 1703, 1715, 1720, 186, 839, 140, 1733, + 102, 1723, 1771, 75, 1187, 1808, 1812, 1824, 1836, 1840, + 1852, 1864, 892, 0, 1857, 1876, 1880, 1888, 0, 1214, + 1900, 1912, 1916, 1928, 58, 1940, 1952, 1956, 1968, 1973, + 1288, 1985, 1989, 2001, 966, 2013, 2017, 2029, 2041, 2045, + + 2057, 2069, 611, 2081, 2062, 2093, 2105, 52, 2098, 2117, + 2129, 1093, 647, 2229, 2142, 2146, 2150, 2154, 2158, 2162, + 2164, 2168, 2172, 2176, 2180, 2184, 2188, 2192, 2196, 2200, + 2204, 2208, 2212, 2216, 2220, 2224 + } ; + +static yyconst flex_int16_t yy_def[337] = + { 0, + 314, 1, 314, 315, 315, 314, 316, 317, 315, 314, + 314, 314, 10, 10, 10, 10, 10, 315, 315, 314, + 11, 315, 316, 317, 315, 23, 10, 11, 314, 318, + 11, 11, 314, 12, 10, 10, 10, 10, 10, 10, + 314, 315, 26, 319, 319, 29, 314, 29, 29, 314, + 315, 315, 10, 10, 10, 10, 10, 10, 315, 314, + 319, 47, 319, 314, 319, 319, 29, 50, 10, 10, + 315, 71, 320, 321, 23, 75, 11, 10, 10, 315, + 60, 319, 314, 319, 314, 319, 319, 29, 10, 10, + 322, 321, 322, 323, 315, 77, 318, 77, 33, 10, + + 10, 315, 315, 314, 319, 314, 319, 319, 314, 314, + 319, 314, 319, 319, 29, 23, 324, 314, 323, 322, + 325, 323, 325, 71, 10, 10, 315, 104, 314, 326, + 104, 104, 319, 314, 319, 314, 319, 319, 314, 314, + 29, 23, 324, 327, 324, 327, 10, 10, 328, 329, + 329, 129, 314, 129, 129, 314, 315, 319, 314, 319, + 319, 314, 314, 319, 314, 23, 10, 315, 11, 328, + 330, 329, 153, 329, 329, 329, 129, 329, 156, 319, + 314, 23, 23, 11, 11, 330, 315, 329, 329, 329, + 329, 129, 23, 23, 11, 329, 329, 329, 329, 329, + + 329, 129, 315, 314, 23, 11, 329, 329, 329, 329, + 129, 331, 204, 314, 332, 204, 204, 314, 23, 11, + 329, 329, 329, 329, 333, 333, 214, 314, 214, 214, + 314, 331, 315, 314, 11, 329, 333, 228, 333, 333, + 333, 214, 231, 334, 234, 314, 335, 234, 234, 314, + 11, 333, 333, 333, 333, 214, 336, 336, 246, 314, + 246, 246, 314, 334, 11, 333, 333, 333, 333, 333, + 333, 214, 336, 260, 336, 336, 336, 246, 263, 11, + 333, 333, 333, 333, 214, 336, 336, 336, 336, 246, + 11, 333, 333, 333, 333, 336, 336, 336, 336, 336, + + 336, 246, 333, 336, 336, 336, 336, 246, 336, 336, + 336, 336, 336, 0, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314 + } ; + +static yyconst flex_int16_t yy_nxt[2268] = + { 0, + 4, 5, 6, 4, 7, 8, 9, 4, 4, 7, + 7, 7, 10, 4, 4, 11, 4, 10, 12, 4, + 4, 7, 10, 13, 14, 10, 10, 10, 10, 10, + 10, 10, 15, 16, 10, 10, 17, 10, 19, 20, + 20, 20, 39, 22, 40, 20, 20, 51, 19, 20, + 31, 72, 21, 18, 22, 117, 18, 21, 18, 18, + 18, 18, 21, 118, 261, 77, 18, 18, 21, 18, + 229, 18, 18, 18, 18, 22, 264, 18, 23, 18, + 18, 18, 18, 23, 26, 23, 27, 18, 18, 21, + 18, 27, 18, 18, 18, 23, 27, 27, 27, 27, + + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 18, 28, 261, 18, 29, 18, 18, 18, + 18, 30, 31, 31, 32, 18, 18, 18, 18, 31, + 18, 33, 18, 30, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 18, 18, 261, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 34, 18, 18, 18, 18, 34, 18, 18, + 18, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 18, 22, + 273, 18, 232, 18, 18, 18, 18, 45, 103, 229, + + 61, 18, 18, 21, 18, 184, 18, 18, 18, 43, + 45, 229, 104, 61, 43, 157, 185, 237, 131, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 44, 45, 154, 44, 46, + 44, 44, 44, 44, 47, 48, 48, 49, 44, 44, + 44, 44, 48, 44, 50, 44, 47, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 18, 51, 117, 18, 117, 18, 18, + 18, 18, 103, 118, 168, 118, 18, 18, 18, 18, + 168, 18, 18, 18, 51, 219, 104, 31, 169, 151, + + 45, 205, 172, 61, 169, 31, 18, 51, 187, 18, + 33, 18, 18, 18, 18, 18, 33, 33, 33, 18, + 18, 18, 18, 33, 18, 33, 18, 18, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 44, 45, 194, 44, 62, 44, + 44, 44, 44, 47, 47, 47, 47, 44, 44, 44, + 44, 47, 44, 44, 44, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 63, 64, 65, 63, 64, 65, 61, 66, + 193, 151, 187, 67, 44, 45, 82, 44, 68, 44, + + 44, 44, 44, 44, 50, 50, 50, 44, 44, 44, + 44, 50, 44, 50, 44, 44, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 71, 72, 73, 71, 164, 71, 71, 74, + 71, 75, 76, 75, 180, 71, 71, 77, 71, 171, + 71, 71, 71, 18, 22, 183, 18, 23, 18, 18, + 18, 18, 23, 23, 23, 81, 18, 18, 21, 18, + 81, 18, 18, 18, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 64, 64, 64, 65, 64, 65, 61, 65, 64, + + 65, 61, 83, 182, 157, 82, 84, 85, 86, 82, + 154, 124, 151, 87, 31, 172, 203, 88, 71, 71, + 73, 71, 31, 71, 71, 74, 71, 71, 71, 71, + 204, 71, 71, 71, 71, 203, 71, 71, 71, 93, + 178, 18, 94, 172, 84, 85, 86, 61, 154, 204, + 95, 71, 72, 73, 71, 108, 71, 71, 74, 71, + 75, 75, 75, 184, 71, 71, 77, 71, 165, 71, + 71, 71, 43, 224, 185, 232, 181, 43, 216, 172, + 171, 236, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 71, 96, + + 73, 71, 167, 71, 71, 74, 71, 97, 98, 98, + 233, 71, 71, 71, 71, 295, 71, 99, 71, 105, + 106, 105, 61, 303, 234, 93, 107, 106, 106, 106, + 108, 85, 85, 85, 109, 93, 151, 226, 110, 172, + 237, 166, 110, 86, 85, 86, 61, 86, 85, 86, + 61, 312, 48, 149, 108, 111, 112, 113, 108, 313, + 233, 93, 114, 18, 94, 120, 115, 121, 122, 148, + 157, 147, 95, 131, 234, 124, 123, 71, 124, 73, + 71, 131, 71, 71, 74, 71, 97, 97, 97, 93, + 71, 71, 71, 71, 93, 71, 71, 71, 71, 124, + + 73, 71, 142, 71, 71, 74, 71, 71, 99, 99, + 127, 71, 71, 71, 71, 126, 71, 99, 71, 18, + 128, 125, 18, 129, 18, 18, 18, 18, 130, 131, + 131, 132, 18, 18, 18, 18, 131, 18, 33, 18, + 130, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 105, 106, 105, + 61, 106, 106, 106, 105, 106, 105, 61, 133, 106, + 106, 106, 134, 116, 55, 133, 135, 136, 135, 61, + 134, 102, 101, 137, 136, 136, 136, 138, 100, 91, + 90, 139, 112, 112, 112, 140, 111, 112, 113, 61, + + 89, 226, 264, 140, 237, 248, 80, 138, 113, 112, + 113, 61, 113, 112, 113, 61, 55, 79, 120, 138, + 144, 145, 78, 138, 111, 112, 113, 141, 70, 146, + 120, 114, 121, 122, 120, 115, 121, 122, 69, 51, + 258, 123, 31, 273, 51, 123, 150, 151, 195, 150, + 152, 150, 150, 150, 150, 153, 154, 154, 155, 150, + 150, 150, 150, 154, 150, 156, 150, 153, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 18, 157, 48, 18, 48, 18, + 18, 18, 18, 258, 61, 60, 273, 18, 18, 18, + + 18, 59, 18, 18, 18, 158, 159, 158, 61, 58, + 57, 56, 160, 159, 159, 159, 161, 55, 54, 53, + 162, 136, 136, 136, 163, 135, 136, 135, 61, 52, + 42, 41, 163, 136, 136, 136, 161, 135, 136, 135, + 61, 38, 37, 120, 163, 144, 145, 36, 161, 135, + 136, 135, 164, 35, 146, 120, 137, 144, 145, 25, + 138, 136, 136, 136, 165, 314, 146, 226, 139, 314, + 237, 314, 140, 150, 151, 314, 150, 173, 150, 150, + 150, 150, 153, 153, 153, 153, 150, 150, 150, 150, + 153, 150, 150, 150, 153, 153, 153, 153, 153, 153, + + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 174, 64, 175, 158, 159, 158, 61, 176, 314, + 314, 314, 177, 150, 178, 180, 150, 179, 150, 150, + 150, 150, 150, 156, 156, 156, 150, 150, 150, 150, + 156, 150, 156, 150, 150, 156, 156, 156, 156, 156, + 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, + 156, 159, 159, 159, 158, 159, 158, 61, 314, 159, + 159, 159, 181, 314, 314, 180, 158, 159, 158, 164, + 181, 51, 51, 160, 31, 31, 314, 161, 159, 159, + 159, 165, 206, 220, 258, 162, 314, 273, 314, 163, + + 174, 64, 175, 172, 175, 64, 175, 172, 314, 232, + 314, 188, 216, 314, 314, 188, 175, 64, 175, 172, + 216, 189, 85, 190, 314, 314, 51, 188, 191, 31, + 314, 314, 192, 196, 106, 196, 172, 235, 314, 314, + 197, 314, 314, 314, 198, 189, 85, 190, 172, 190, + 85, 190, 172, 314, 264, 314, 198, 248, 314, 314, + 198, 190, 85, 190, 172, 248, 199, 112, 200, 314, + 314, 314, 198, 201, 314, 314, 314, 202, 196, 106, + 196, 172, 196, 106, 196, 172, 314, 51, 51, 207, + 31, 31, 314, 207, 208, 136, 208, 172, 265, 280, + + 314, 209, 314, 314, 314, 210, 199, 112, 200, 172, + 200, 112, 200, 172, 314, 51, 314, 210, 31, 314, + 314, 210, 200, 112, 200, 172, 291, 208, 136, 208, + 172, 314, 314, 210, 199, 112, 200, 211, 223, 314, + 314, 201, 314, 314, 314, 202, 212, 213, 314, 212, + 214, 212, 212, 212, 212, 215, 216, 216, 217, 212, + 212, 212, 212, 216, 212, 218, 212, 215, 216, 216, + 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, + 216, 216, 216, 216, 221, 159, 221, 172, 314, 51, + 314, 222, 31, 314, 314, 223, 208, 136, 208, 172, + + 31, 221, 159, 221, 172, 314, 314, 223, 208, 136, + 208, 224, 236, 314, 314, 209, 314, 314, 314, 210, + 225, 226, 314, 225, 227, 225, 225, 225, 225, 228, + 229, 229, 230, 225, 225, 225, 225, 229, 225, 231, + 225, 228, 229, 229, 229, 229, 229, 229, 229, 229, + 229, 229, 229, 229, 229, 229, 229, 229, 212, 232, + 314, 212, 314, 212, 212, 212, 212, 314, 314, 314, + 314, 212, 212, 212, 212, 314, 212, 212, 212, 212, + 232, 314, 212, 218, 212, 212, 212, 212, 212, 218, + 218, 218, 212, 212, 212, 212, 218, 212, 218, 212, + + 212, 218, 218, 218, 218, 218, 218, 218, 218, 218, + 218, 218, 218, 218, 218, 218, 218, 221, 159, 221, + 172, 314, 314, 314, 51, 314, 314, 31, 236, 221, + 159, 221, 224, 251, 314, 31, 222, 314, 314, 314, + 223, 225, 226, 314, 225, 238, 225, 225, 225, 225, + 228, 228, 228, 228, 225, 225, 225, 225, 228, 225, + 225, 225, 228, 228, 228, 228, 228, 228, 228, 228, + 228, 228, 228, 228, 228, 228, 228, 228, 228, 239, + 64, 240, 239, 64, 240, 237, 241, 314, 314, 314, + 242, 225, 226, 252, 225, 243, 225, 225, 225, 225, + + 225, 231, 231, 231, 225, 225, 225, 225, 231, 225, + 231, 225, 225, 231, 231, 231, 231, 231, 231, 231, + 231, 231, 231, 231, 231, 231, 231, 231, 231, 244, + 245, 314, 244, 246, 244, 244, 244, 244, 247, 248, + 248, 249, 244, 244, 244, 244, 248, 244, 250, 244, + 247, 248, 248, 248, 248, 248, 248, 248, 248, 248, + 248, 248, 248, 248, 248, 248, 248, 240, 64, 240, + 237, 240, 64, 240, 237, 314, 314, 314, 252, 253, + 85, 254, 252, 314, 314, 314, 255, 314, 314, 314, + 256, 257, 258, 314, 257, 259, 257, 257, 257, 257, + + 260, 261, 261, 262, 257, 257, 257, 257, 261, 257, + 263, 257, 260, 261, 261, 261, 261, 261, 261, 261, + 261, 261, 261, 261, 261, 261, 261, 261, 261, 244, + 264, 314, 244, 314, 244, 244, 244, 244, 314, 314, + 314, 314, 244, 244, 244, 244, 314, 244, 244, 244, + 244, 264, 314, 244, 250, 244, 244, 244, 244, 244, + 250, 250, 250, 244, 244, 244, 244, 250, 244, 250, + 244, 244, 250, 250, 250, 250, 250, 250, 250, 250, + 250, 250, 250, 250, 250, 250, 250, 250, 266, 106, + 266, 237, 314, 314, 314, 267, 314, 314, 314, 268, + + 253, 85, 254, 237, 254, 85, 254, 237, 314, 314, + 314, 268, 314, 314, 314, 268, 254, 85, 254, 237, + 314, 269, 112, 270, 275, 64, 276, 268, 271, 314, + 314, 277, 272, 257, 258, 278, 257, 274, 257, 257, + 257, 257, 260, 260, 260, 260, 257, 257, 257, 257, + 260, 257, 257, 257, 260, 260, 260, 260, 260, 260, + 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, + 260, 257, 258, 314, 257, 279, 257, 257, 257, 257, + 257, 263, 263, 263, 257, 257, 257, 257, 263, 257, + 263, 257, 257, 263, 263, 263, 263, 263, 263, 263, + + 263, 263, 263, 263, 263, 263, 263, 263, 263, 266, + 106, 266, 237, 266, 106, 266, 237, 314, 314, 314, + 281, 314, 314, 314, 281, 282, 136, 282, 237, 314, + 314, 314, 283, 314, 314, 314, 284, 269, 112, 270, + 237, 270, 112, 270, 237, 314, 314, 314, 284, 314, + 314, 314, 284, 270, 112, 270, 237, 314, 275, 64, + 276, 273, 314, 314, 284, 269, 112, 270, 285, 286, + 314, 314, 271, 314, 314, 314, 272, 276, 64, 276, + 273, 276, 64, 276, 273, 314, 314, 314, 286, 287, + 85, 288, 286, 314, 314, 314, 289, 314, 314, 314, + + 290, 292, 159, 292, 237, 314, 314, 314, 293, 314, + 314, 314, 294, 282, 136, 282, 237, 282, 136, 282, + 237, 314, 314, 314, 294, 314, 314, 314, 294, 282, + 136, 282, 295, 314, 314, 314, 283, 314, 314, 314, + 284, 296, 106, 296, 273, 314, 314, 314, 297, 314, + 314, 314, 298, 287, 85, 288, 273, 288, 85, 288, + 273, 314, 314, 314, 298, 314, 314, 314, 298, 288, + 85, 288, 273, 314, 299, 112, 300, 314, 314, 314, + 298, 301, 314, 314, 314, 302, 292, 159, 292, 237, + 292, 159, 292, 237, 314, 314, 314, 303, 314, 314, + + 314, 303, 292, 159, 292, 295, 314, 314, 314, 293, + 314, 314, 314, 294, 296, 106, 296, 273, 296, 106, + 296, 273, 314, 314, 314, 304, 314, 314, 314, 304, + 305, 136, 305, 273, 314, 314, 314, 306, 314, 314, + 314, 307, 299, 112, 300, 273, 300, 112, 300, 273, + 314, 314, 314, 307, 314, 314, 314, 307, 300, 112, + 300, 273, 314, 305, 136, 305, 273, 314, 314, 307, + 299, 112, 300, 308, 311, 314, 314, 301, 314, 314, + 314, 302, 309, 159, 309, 273, 314, 314, 314, 310, + 314, 314, 314, 311, 305, 136, 305, 273, 314, 309, + + 159, 309, 273, 314, 314, 311, 305, 136, 305, 312, + 313, 314, 314, 306, 314, 314, 314, 307, 309, 159, + 309, 273, 314, 314, 314, 314, 314, 314, 314, 313, + 309, 159, 309, 312, 314, 314, 314, 310, 314, 314, + 314, 311, 18, 314, 18, 18, 23, 314, 23, 23, + 24, 314, 24, 24, 30, 314, 30, 30, 44, 314, + 44, 44, 73, 73, 92, 92, 92, 92, 93, 93, + 314, 93, 119, 119, 119, 119, 143, 143, 143, 143, + 121, 121, 121, 121, 130, 314, 130, 130, 144, 144, + 144, 144, 170, 314, 170, 170, 150, 314, 150, 150, + + 186, 314, 186, 186, 212, 314, 212, 212, 215, 314, + 215, 215, 225, 314, 225, 225, 244, 314, 244, 244, + 247, 314, 247, 247, 257, 314, 257, 257, 3, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314 + } ; + +static yyconst flex_int16_t yy_chk[2268] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, + 6, 6, 17, 22, 17, 20, 20, 32, 19, 19, + 32, 72, 5, 7, 7, 91, 7, 22, 7, 7, + 7, 7, 19, 91, 308, 72, 7, 7, 7, 7, + 285, 7, 7, 7, 10, 10, 264, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 11, 11, 261, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 12, 12, 259, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 23, 23, + 257, 23, 232, 23, 23, 23, 23, 45, 81, 229, + + 45, 23, 23, 23, 23, 169, 23, 23, 23, 26, + 61, 227, 81, 61, 26, 132, 169, 225, 132, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 29, 29, 211, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 30, 30, 93, 30, 120, 30, 30, + 30, 30, 103, 93, 148, 120, 30, 30, 30, 30, + 168, 30, 30, 30, 31, 205, 103, 31, 148, 151, + + 164, 194, 151, 164, 168, 31, 33, 33, 186, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 47, 47, 183, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 49, 49, 49, 63, 63, 63, 63, 49, + 182, 179, 171, 49, 50, 50, 63, 50, 50, 50, + + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 55, 55, 55, 55, 180, 55, 55, 55, + 55, 55, 55, 55, 180, 55, 55, 55, 55, 170, + 55, 55, 55, 60, 60, 167, 60, 60, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 64, 64, 64, 65, 65, 65, 65, 66, 66, + + 66, 66, 64, 166, 157, 65, 67, 67, 67, 66, + 154, 98, 172, 67, 98, 172, 193, 67, 71, 71, + 71, 71, 98, 71, 71, 71, 71, 71, 71, 71, + 193, 71, 71, 71, 71, 203, 71, 71, 71, 74, + 178, 74, 74, 178, 84, 84, 84, 84, 152, 203, + 74, 75, 75, 75, 75, 84, 75, 75, 75, 75, + 75, 75, 75, 184, 75, 75, 75, 75, 181, 75, + 75, 75, 76, 236, 184, 217, 181, 76, 217, 150, + 149, 236, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 77, 77, + + 77, 77, 147, 77, 77, 77, 77, 77, 77, 77, + 219, 77, 77, 77, 77, 303, 77, 77, 77, 82, + 82, 82, 82, 303, 219, 146, 82, 83, 83, 83, + 82, 85, 85, 85, 83, 144, 224, 226, 83, 224, + 226, 142, 85, 86, 86, 86, 86, 87, 87, 87, + 87, 313, 141, 127, 86, 88, 88, 88, 87, 313, + 233, 92, 88, 92, 92, 94, 88, 94, 94, 126, + 131, 125, 92, 131, 233, 124, 94, 97, 97, 97, + 97, 131, 97, 97, 97, 97, 97, 97, 97, 123, + 97, 97, 97, 97, 121, 97, 97, 97, 99, 99, + + 99, 99, 116, 99, 99, 99, 99, 99, 99, 99, + 102, 99, 99, 99, 99, 101, 99, 99, 99, 104, + 104, 100, 104, 104, 104, 104, 104, 104, 104, 104, + 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, + 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, + 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, + 105, 106, 106, 106, 107, 107, 107, 107, 105, 109, + 109, 109, 106, 90, 89, 107, 108, 108, 108, 108, + 109, 80, 79, 108, 110, 110, 110, 108, 78, 73, + 70, 110, 112, 112, 112, 110, 111, 111, 111, 111, + + 69, 237, 249, 112, 237, 249, 59, 111, 113, 113, + 113, 113, 114, 114, 114, 114, 58, 57, 117, 113, + 117, 117, 56, 114, 115, 115, 115, 115, 54, 117, + 119, 115, 119, 119, 122, 115, 122, 122, 53, 185, + 258, 119, 185, 258, 51, 122, 129, 129, 185, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 130, 130, 48, 130, 46, 130, + 130, 130, 130, 273, 44, 43, 273, 130, 130, 130, + + 130, 42, 130, 130, 130, 133, 133, 133, 133, 40, + 39, 38, 133, 134, 134, 134, 133, 37, 36, 35, + 134, 136, 136, 136, 134, 135, 135, 135, 135, 34, + 25, 24, 136, 139, 139, 139, 135, 137, 137, 137, + 137, 16, 15, 143, 139, 143, 143, 14, 137, 138, + 138, 138, 138, 13, 143, 145, 138, 145, 145, 9, + 138, 140, 140, 140, 140, 3, 145, 295, 140, 0, + 295, 0, 140, 153, 153, 0, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 155, 155, 155, 158, 158, 158, 158, 155, 0, + 0, 0, 155, 156, 156, 158, 156, 156, 156, 156, + 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, + 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, + 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, + 156, 159, 159, 159, 160, 160, 160, 160, 0, 162, + 162, 162, 159, 0, 0, 160, 161, 161, 161, 161, + 162, 195, 206, 161, 195, 206, 0, 161, 163, 163, + 163, 163, 195, 206, 312, 163, 0, 312, 0, 163, + + 174, 174, 174, 174, 175, 175, 175, 175, 0, 216, + 0, 174, 216, 0, 0, 175, 176, 176, 176, 176, + 216, 177, 177, 177, 0, 0, 220, 176, 177, 220, + 0, 0, 177, 188, 188, 188, 188, 220, 0, 0, + 188, 0, 0, 0, 188, 189, 189, 189, 189, 190, + 190, 190, 190, 0, 248, 0, 189, 248, 0, 0, + 190, 191, 191, 191, 191, 248, 192, 192, 192, 0, + 0, 0, 191, 192, 0, 0, 0, 192, 196, 196, + 196, 196, 197, 197, 197, 197, 0, 251, 265, 196, + 251, 265, 0, 197, 198, 198, 198, 198, 251, 265, + + 0, 198, 0, 0, 0, 198, 199, 199, 199, 199, + 200, 200, 200, 200, 0, 280, 0, 199, 280, 0, + 0, 200, 201, 201, 201, 201, 280, 208, 208, 208, + 208, 0, 0, 201, 202, 202, 202, 202, 208, 0, + 0, 202, 0, 0, 0, 202, 204, 204, 0, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 207, 207, 207, 207, 0, 291, + 0, 207, 291, 0, 0, 207, 209, 209, 209, 209, + + 291, 221, 221, 221, 221, 0, 0, 209, 210, 210, + 210, 210, 221, 0, 0, 210, 0, 0, 0, 210, + 214, 214, 0, 214, 214, 214, 214, 214, 214, 214, + 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, + 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, + 214, 214, 214, 214, 214, 214, 214, 214, 215, 215, + 0, 215, 0, 215, 215, 215, 215, 0, 0, 0, + 0, 215, 215, 215, 215, 0, 215, 215, 215, 218, + 218, 0, 218, 218, 218, 218, 218, 218, 218, 218, + 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, + + 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, + 218, 218, 218, 218, 218, 218, 218, 222, 222, 222, + 222, 0, 0, 0, 235, 0, 0, 235, 222, 223, + 223, 223, 223, 235, 0, 235, 223, 0, 0, 0, + 223, 228, 228, 0, 228, 228, 228, 228, 228, 228, + 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, + 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, + 228, 228, 228, 228, 228, 228, 228, 228, 228, 230, + 230, 230, 239, 239, 239, 239, 230, 0, 0, 0, + 230, 231, 231, 239, 231, 231, 231, 231, 231, 231, + + 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, + 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, + 231, 231, 231, 231, 231, 231, 231, 231, 231, 234, + 234, 0, 234, 234, 234, 234, 234, 234, 234, 234, + 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, + 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, + 234, 234, 234, 234, 234, 234, 234, 240, 240, 240, + 240, 241, 241, 241, 241, 0, 0, 0, 240, 242, + 242, 242, 241, 0, 0, 0, 242, 0, 0, 0, + 242, 246, 246, 0, 246, 246, 246, 246, 246, 246, + + 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, + 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, + 246, 246, 246, 246, 246, 246, 246, 246, 246, 247, + 247, 0, 247, 0, 247, 247, 247, 247, 0, 0, + 0, 0, 247, 247, 247, 247, 0, 247, 247, 247, + 250, 250, 0, 250, 250, 250, 250, 250, 250, 250, + 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, + 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, + 250, 250, 250, 250, 250, 250, 250, 250, 252, 252, + 252, 252, 0, 0, 0, 252, 0, 0, 0, 252, + + 253, 253, 253, 253, 254, 254, 254, 254, 0, 0, + 0, 253, 0, 0, 0, 254, 255, 255, 255, 255, + 0, 256, 256, 256, 262, 262, 262, 255, 256, 0, + 0, 262, 256, 260, 260, 262, 260, 260, 260, 260, + 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, + 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, + 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, + 260, 263, 263, 0, 263, 263, 263, 263, 263, 263, + 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, + 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, + + 263, 263, 263, 263, 263, 263, 263, 263, 263, 266, + 266, 266, 266, 267, 267, 267, 267, 0, 0, 0, + 266, 0, 0, 0, 267, 268, 268, 268, 268, 0, + 0, 0, 268, 0, 0, 0, 268, 269, 269, 269, + 269, 270, 270, 270, 270, 0, 0, 0, 269, 0, + 0, 0, 270, 271, 271, 271, 271, 0, 275, 275, + 275, 275, 0, 0, 271, 272, 272, 272, 272, 275, + 0, 0, 272, 0, 0, 0, 272, 276, 276, 276, + 276, 277, 277, 277, 277, 0, 0, 0, 276, 278, + 278, 278, 277, 0, 0, 0, 278, 0, 0, 0, + + 278, 281, 281, 281, 281, 0, 0, 0, 281, 0, + 0, 0, 281, 282, 282, 282, 282, 283, 283, 283, + 283, 0, 0, 0, 282, 0, 0, 0, 283, 284, + 284, 284, 284, 0, 0, 0, 284, 0, 0, 0, + 284, 286, 286, 286, 286, 0, 0, 0, 286, 0, + 0, 0, 286, 287, 287, 287, 287, 288, 288, 288, + 288, 0, 0, 0, 287, 0, 0, 0, 288, 289, + 289, 289, 289, 0, 290, 290, 290, 0, 0, 0, + 289, 290, 0, 0, 0, 290, 292, 292, 292, 292, + 293, 293, 293, 293, 0, 0, 0, 292, 0, 0, + + 0, 293, 294, 294, 294, 294, 0, 0, 0, 294, + 0, 0, 0, 294, 296, 296, 296, 296, 297, 297, + 297, 297, 0, 0, 0, 296, 0, 0, 0, 297, + 298, 298, 298, 298, 0, 0, 0, 298, 0, 0, + 0, 298, 299, 299, 299, 299, 300, 300, 300, 300, + 0, 0, 0, 299, 0, 0, 0, 300, 301, 301, + 301, 301, 0, 305, 305, 305, 305, 0, 0, 301, + 302, 302, 302, 302, 305, 0, 0, 302, 0, 0, + 0, 302, 304, 304, 304, 304, 0, 0, 0, 304, + 0, 0, 0, 304, 306, 306, 306, 306, 0, 309, + + 309, 309, 309, 0, 0, 306, 307, 307, 307, 307, + 309, 0, 0, 307, 0, 0, 0, 307, 310, 310, + 310, 310, 0, 0, 0, 0, 0, 0, 0, 310, + 311, 311, 311, 311, 0, 0, 0, 311, 0, 0, + 0, 311, 315, 0, 315, 315, 316, 0, 316, 316, + 317, 0, 317, 317, 318, 0, 318, 318, 319, 0, + 319, 319, 320, 320, 321, 321, 321, 321, 322, 322, + 0, 322, 323, 323, 323, 323, 324, 324, 324, 324, + 325, 325, 325, 325, 326, 0, 326, 326, 327, 327, + 327, 327, 328, 0, 328, 328, 329, 0, 329, 329, + + 330, 0, 330, 330, 331, 0, 331, 331, 332, 0, + 332, 332, 333, 0, 333, 333, 334, 0, 334, 334, + 335, 0, 335, 335, 336, 0, 336, 336, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +extern int yy_flex_debug; +int yy_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "config.l" +/*************************************************************************** + config.l (C) 1999 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ +#line 29 "config.l" +#include +#include +#include +#include +#include +#include + +/* unistd override for GNU flex for non-UNIX systems */ +#ifndef HAVE_UNISTD_H +# define YY_NO_UNISTD_H +#endif + +#ifdef _MSC_VER +# include +# include +#endif + +config_entry_t *conf; +int cur_section=0; /* Size-1 and current section in conf */ +char *exported_conf_path; /* Path which the config file was found in */ +int dospath; /* Use dos-style paths? */ + +typedef struct { + const char *name; + int value; +} name_value_pair; + + +struct { + const char *name; + void *(*check_driver)(char *name); +} freesci_subsystems[FREESCI_DRIVER_SUBSYSTEMS_NR] = { + {"gfx", NULL}, +#ifdef __GNUC__ +#warning "sound" +#endif +#if 0 + {"pcm", parse_pcmout_driver}, + {"midiout", parse_midiout_driver} +#endif +}; + + +typedef struct _file_stack { + char *name; + YY_BUFFER_STATE handle; + struct _file_stack *next; +} file_stack_t; + +static file_stack_t *file_stack = NULL; +static char *yy_filename = NULL; +static YY_BUFFER_STATE yy_fsci_active_buffer; + +static int +push_file(char *name); + +static int +pop_file(void); + +FILE *initial_file; + +static void +set_config_parameter(config_entry_t *conf, char *subsystem_name, char *driver_name, + char *option, char *value); + + +static int +parse_name(char *name, name_value_pair* nvps, const char *what, int oldval); /* Parses a string with a name value pair */ + +static void +copy_subsystem_options(config_entry_t *dest, config_entry_t *src); +/* Copies all subsystem options +** Parameters: (config_entry_t *) dest: The destination config struct +** (config_entry_t *) src: Source struct +** Returns : (void) +*/ + + +static name_value_pair valid_modes[] = { + {"default", GFXR_DITHER_MODE_D16}, + {"dither", GFXR_DITHER_MODE_D16}, + {"dither16", GFXR_DITHER_MODE_D16}, + {"dither_16", GFXR_DITHER_MODE_D16}, + {"dither-16", GFXR_DITHER_MODE_D16}, + {"d16", GFXR_DITHER_MODE_D16}, + {"flat", GFXR_DITHER_MODE_F256}, + {"interpol", GFXR_DITHER_MODE_F256}, + {"interpolate", GFXR_DITHER_MODE_F256}, + {"dither256", GFXR_DITHER_MODE_D256}, + {"dither_256", GFXR_DITHER_MODE_D256}, + {"d256", GFXR_DITHER_MODE_D256}, + {0, 0} /* Terminator */ +}; + +static name_value_pair yesno[] = { + {"yes", 1}, + {"no", 0}, + {"true", 1}, + {"false", 0}, + {"1", 1}, + {"0", 0}, + {"ok", 1}, + {"enable", 1}, + {"disable", 0}, + {"activate", 1}, + {"deactivate", 0}, + {"+", 1}, + {"-", 0}, + {"on", 1}, + {"off", 0}, + {0, 0} +}; + +static name_value_pair dither_pattern[] = { + {"scaled", GFXR_DITHER_PATTERN_SCALED}, + {"unscaled", GFXR_DITHER_PATTERN_1}, + {"one", GFXR_DITHER_PATTERN_1}, + {"1", GFXR_DITHER_PATTERN_1}, + {0, 0} +}; + +static name_value_pair dirty_strategy[] = { + {"1", GFXOP_DIRTY_FRAMES_ONE}, + {"one", GFXOP_DIRTY_FRAMES_ONE}, + {"cluster", GFXOP_DIRTY_FRAMES_CLUSTERS}, + {"clusters", GFXOP_DIRTY_FRAMES_CLUSTERS}, + {0, 0} +}; + +static name_value_pair brush_mode[] = { + {"scaled", GFX_BRUSH_MODE_SCALED}, + {"ellipse", GFX_BRUSH_MODE_ELLIPSES}, + {"ellipses", GFX_BRUSH_MODE_ELLIPSES}, + {"rnd_ellipses", GFX_BRUSH_MODE_RANDOM_ELLIPSES}, + {"rnd-ellipses", GFX_BRUSH_MODE_RANDOM_ELLIPSES}, + {"random_ellipses", GFX_BRUSH_MODE_RANDOM_ELLIPSES}, + {"random-ellipses", GFX_BRUSH_MODE_RANDOM_ELLIPSES}, + {"morerandom", GFX_BRUSH_MODE_MORERANDOM}, + {"more-random", GFX_BRUSH_MODE_MORERANDOM}, + {"more_random", GFX_BRUSH_MODE_MORERANDOM}, + {0, 0} +}; + +static name_value_pair filter_mode[] = { + {"none", GFX_XLATE_FILTER_NONE}, + {"linear", GFX_XLATE_FILTER_LINEAR}, + {"bilinear", GFX_XLATE_FILTER_LINEAR}, + {"bi-linear", GFX_XLATE_FILTER_LINEAR}, + {"trilinear", GFX_XLATE_FILTER_TRILINEAR}, + {"tri-linear", GFX_XLATE_FILTER_TRILINEAR}, + {0, 0} +}; + +static name_value_pair antialiasing_modes[] = { + {"none", GFXR_ANTIALIASING_NONE}, + {"0", GFXR_ANTIALIASING_NONE}, + {"off", GFXR_ANTIALIASING_NONE}, + {"on", GFXR_ANTIALIASING_SIMPLE}, + {"basic", GFXR_ANTIALIASING_SIMPLE}, + {"simple", GFXR_ANTIALIASING_SIMPLE} +}; + +static name_value_pair line_mode[] = { + {"correct", GFX_LINE_MODE_CORRECT}, + {"normal", GFX_LINE_MODE_CORRECT}, + {"fast", GFX_LINE_MODE_FAST}, + {"half", GFX_LINE_MODE_FAST}, + {"fine", GFX_LINE_MODE_FINE}, + {"thin", GFX_LINE_MODE_FINE}, + {0, 0} +}; + +#define BAD_INT_VALUE -33333333 + +/* Types of options */ +#define OPTION_TYPE_NONE 0 +#define OPTION_TYPE_INT 1 +#define OPTION_TYPE_NVP 2 +#define OPTION_TYPE_INVERSE_NVP 3 +#define OPTION_TYPE_STATICREF 4 +#define OPTION_TYPE_STRING 5 +#define OPTION_TYPE_RECT 6 + +typedef struct { + int type; + const char *name; + int min; + int max; + name_value_pair *nvp; + void * (*parse_funct)(char *); + int varoffset; +} standard_option; + +#define OPT_END {OPTION_TYPE_NONE, NULL, 0, 0, NULL, 0} +/* Terminates */ + +#define OPT_INT(NAME, VARNAME, MIN, MAX) {OPTION_TYPE_INT, NAME, MIN, MAX, NULL, NULL, offsetof (config_entry_t, VARNAME)} +/* Read INT from the interval [MIN, MAX] */ + +#define OPT_STRING(NAME, VARNAME) {OPTION_TYPE_STRING, NAME, 0, 0, NULL, NULL, offsetof (config_entry_t, VARNAME)} +/* Read a string */ + +#define OPT_NVP(NAME, VARNAME, NVP) {OPTION_TYPE_NVP, NAME, 0, 0, NVP, NULL, offsetof (config_entry_t, VARNAME)} +/* Read (name,value) pair from NVP */ + +#define OPT_INVERSE_NVP(NAME, VARNAME, NVP) {OPTION_TYPE_INVERSE_NVP, NAME, 0, 0, NVP, NULL, offsetof (config_entry_t, VARNAME)} +/* Read NVP and negate result */ + +#define OPT_STATICREF(NAME, VARNAME, FUNCTN) {OPTION_TYPE_STATICREF, NAME, 0, 0, NULL, FUNCTN, offsetof (config_entry_t, VARNAME)} +/* Call FUNCTN() with the specified value, store resulting NULL* */ + +#define OPT_RECT(NAME, VARNAME) {OPTION_TYPE_RECT, NAME, 0, 0, NULL, NULL, offsetof (config_entry_t, VARNAME)} +/* Read a rectangle */ + + +standard_option standard_options[] = { + OPT_RECT("pic_port_bounds", gfx_options.pic_port_bounds), + OPT_NVP("pic0_dither_mode", gfx_options.pic0_dither_mode, valid_modes), + OPT_NVP("color_mode", gfx_options.pic0_dither_mode, valid_modes), + OPT_NVP("pic0_dither_pattern", gfx_options.pic0_dither_pattern, dither_pattern), + OPT_NVP("pic0_brush_mode", gfx_options.pic0_brush_mode, brush_mode), + OPT_NVP("pic0_line_mode", gfx_options.pic0_line_mode, line_mode), + OPT_NVP("dirty_strategy", gfx_options.dirty_frames, dirty_strategy), + OPT_INVERSE_NVP("pic0_scaled", gfx_options.pic0_unscaled, yesno), + OPT_NVP("pic_filter", gfx_options.pic_xlate_filter, filter_mode), + OPT_NVP("pic_antialiasing", gfx_options.pic0_antialiasing, antialiasing_modes), + OPT_NVP("text_filter", gfx_options.text_xlate_filter, filter_mode), + OPT_NVP("view_filter", gfx_options.view_xlate_filter, filter_mode), + OPT_NVP("cursor_filter", gfx_options.cursor_xlate_filter, filter_mode), + OPT_NVP("mouse", mouse, yesno), + OPT_NVP("reverse_stereo", reverse_stereo, yesno), + OPT_INT("pic_buffer_size", gfx_options.buffer_pics_nr, 0, 4096), + OPT_INT("alpha_threshold", alpha_threshold, 0, 255), + OPT_INT("animation_delay", animation_delay, 0, 1000000), + OPT_INT("animation_granularity", animation_granularity, 1, 160), +#ifdef __GNUC__ +#warning "Re-enable sound server config" +#endif +#if 0 + OPT_STATICREF("midiout_driver", midiout_driver, parse_midiout_driver), + OPT_STATICREF("midi_device", midi_device, parse_midi_device), + OPT_STATICREF("sound_server", sound_server, parse_sound_server), + OPT_STATICREF("pcmout_driver", pcmout_driver, parse_pcmout_driver), + OPT_INT("pcmout_rate", pcmout_rate, 11025, 48000), + OPT_INT("pcmout_stereo", pcmout_stereo, 0, 1), +#endif + OPT_STRING("console_log", console_log), + OPT_STRING("module_path", module_path), + OPT_STRING("menu_dir", menu_dir), + OPT_STRING("gfx_driver", gfx_driver_name), + OPT_INT("scale_x", x_scale, 1, 256), + OPT_INT("scale_y", y_scale, 1, 256), + OPT_INT("scale", scale, 1, 256), + OPT_INT("resource_version", res_version, 0, 6), + OPT_INT("color_depth", color_depth, 8, 32), + OPT_END +}; + + +static void +parse_option(char *option, int optlen, char *value); + +char * +crop_value(char *yytext); + +char * +purge_comments(char *comments); + +#line 1353 "config.c" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals (void ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap (void ); +#else +extern int yywrap (void ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex (void); + +#define YY_DECL int yylex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 310 "config.l" + + +#line 1509 "config.c" + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ); + } + + yy_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of yytext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 315 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 2229 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = (yy_hold_char); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 312 "config.l" +{ + char *cleanup; + ++yytext; /* Get over opening bracket */ + + ++cur_section; /* Start new section */ + + /* Create new entry... */ + conf = (config_entry_t *) sci_realloc(conf, sizeof(config_entry_t) * (cur_section + 1)); + + /* ...and initialize it */ + memcpy(&(conf[cur_section]), &(conf[0]), sizeof(config_entry_t)); + if (conf[0].console_log) + conf[cur_section].console_log = sci_strdup (conf[0].console_log); + + /* Copy the subsystem init strings */ + copy_subsystem_options(conf + cur_section, conf); + + while (isspace(*yytext)) + yytext++; + + cleanup = strchr(yytext, ']'); + + do { + *cleanup-- = 0; + } while (isblank(*cleanup)); + + conf[cur_section].name = sci_strdup(yytext); + + conf[cur_section].resource_dir = sci_strdup("."); + +} + YY_BREAK +case 2: +YY_RULE_SETUP +#line 345 "config.l" +{ + + yytext = strchr(yytext, '=') + 1; + + while (isspace(*yytext)) + yytext++; + + version_parse(yytext, &conf[cur_section].version); +} + YY_BREAK +case 3: +YY_RULE_SETUP +#line 355 "config.l" +if (cur_section) { + yytext = strchr(yytext, '=') + 1; + while (isspace(*yytext)) + yytext++; + + sci_free(conf[cur_section].resource_dir); + + conf[cur_section].resource_dir = sci_strdup(yytext); +} + YY_BREAK +case 4: +YY_RULE_SETUP +#line 365 "config.l" +{ + yytext = strchr(yytext, '=') + 1; + + while (isspace(*yytext)) + yytext++; + + strcpy (conf[cur_section].debug_mode, yytext); +} + YY_BREAK +case 5: +YY_RULE_SETUP +#line 374 "config.l" +{ +/* driver parameters */ + char *subsys_name = yytext; + char *driver_name; + char *option, *value; + char *p2; + + yytext = strchr(yytext, '.'); + *yytext++ = 0; + driver_name = yytext; + yytext = strchr(yytext, '.'); + *yytext++ = 0; + + + option = yytext; + yytext = strchr(yytext, '='); + *yytext++ = 0; + + p2 = yytext-2; /* trim right spaces */ + while (p2 > option && isspace (*p2)) + *p2-- = 0; + + value = crop_value(yytext); /* Get config value */ + + set_config_parameter(conf + cur_section, subsys_name, + driver_name, option, value); + +} + YY_BREAK +case 6: +YY_RULE_SETUP +#line 404 "config.l" +{ /* Normal config option */ + char *option_str = yytext; + char *value_str = yytext; + int option_str_len; + + while (isalnum(*value_str) || *value_str == '_') + ++value_str; + + option_str_len = value_str - option_str; + + while (!(isalnum(*value_str) || *value_str == '_' || *value_str == '"' || *value_str == '/' || *value_str == '\\')) + ++value_str; + + value_str = crop_value(value_str); + + parse_option(option_str, option_str_len, value_str); +} + YY_BREAK +case 7: +/* rule 7 can match eol */ +YY_RULE_SETUP +#line 422 "config.l" +{ /* Normal config option */ + char *option_str = yytext; + char *value_str = yytext; + int option_str_len; + + while (isalnum(*value_str) || *value_str == '_') + ++value_str; + + option_str_len = value_str - option_str; + + while (!(isdigit(*value_str)||(*value_str == '"'))) + ++value_str; + + value_str = crop_value(value_str); + + parse_option(option_str, option_str_len, value_str); +} + YY_BREAK +case 8: +/* rule 8 can match eol */ +YY_RULE_SETUP +#line 441 "config.l" +{ + gfx_update_conf(&(conf[cur_section].gfx_options), purge_comments(yytext)); +} + YY_BREAK +case 9: +YY_RULE_SETUP +#line 445 "config.l" +{ + char *filename = strchr(yytext, '<'); + char *end = strchr(filename, '>'); + + *end-- = 0; + while (isblank(*end)) + *end-- = 0; + + filename++; + while (*filename && isblank(*filename)) + filename++; + + push_file(filename); + YY_NEW_FILE; +} + YY_BREAK +case 10: +*yy_cp = (yy_hold_char); /* undo effects of setting up yytext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 462 "config.l" +/* Ignore comments */ + YY_BREAK +case 11: +/* rule 11 can match eol */ +YY_RULE_SETUP +#line 464 "config.l" +/* Eat whitespace */ + YY_BREAK +case YY_STATE_EOF(INITIAL): +#line 466 "config.l" +{ + yy_delete_buffer(YY_CURRENT_BUFFER ); + yyterminate(); +} + YY_BREAK +case 12: +YY_RULE_SETUP +#line 471 "config.l" +printf("Unrecognized option: '%s'\n", yytext); + YY_BREAK +case 13: +YY_RULE_SETUP +#line 473 "config.l" +ECHO; + YY_BREAK +#line 1798 "config.c" + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( yywrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); + register int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart(yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 315 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + register int yy_is_jam; + register char *yy_cp = (yy_c_buf_p); + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 315 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 314); + + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp ) +{ + register char *yy_cp; + + yy_cp = (yy_c_buf_p); + + /* undo effects of setting up yytext */ + *yy_cp = (yy_hold_char); + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = (yy_n_chars) + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + (yytext_ptr) = yy_bp; + (yy_hold_char) = *yy_cp; + (yy_c_buf_p) = yy_cp; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart(yyin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( ) ) + return EOF; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve yytext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ); + } + + yy_init_buffer(YY_CURRENT_BUFFER,input_file ); + yy_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void yy_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * + */ + void yy_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree((void *) b->yy_ch_buf ); + + yyfree((void *) b ); +} + +#ifndef _UNISTD_H /* assume unistd.h has isatty() for us */ +#ifdef __cplusplus +extern "C" { +#endif +#ifdef __THROW /* this is a gnuism */ +extern int isatty (int ) __THROW; +#else +extern int isatty (int ); +#endif +#ifdef __cplusplus +} +#endif +#endif + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + yy_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void yy_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void yypop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param str a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (yyconst char * yystr ) +{ + + return yy_scan_bytes(yystr,strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) yyalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = yytext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int yyget_lineno (void) +{ + + return yylineno; +} + +/** Get the input stream. + * + */ +FILE *yyget_in (void) +{ + return yyin; +} + +/** Get the output stream. + * + */ +FILE *yyget_out (void) +{ + return yyout; +} + +/** Get the length of the current token. + * + */ +int yyget_leng (void) +{ + return yyleng; +} + +/** Get the current token. + * + */ + +char *yyget_text (void) +{ + return yytext; +} + +/** Set the current line number. + * @param line_number + * + */ +void yyset_lineno (int line_number ) +{ + + yylineno = line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * in_str ) +{ + yyin = in_str ; +} + +void yyset_out (FILE * out_str ) +{ + yyout = out_str ; +} + +int yyget_debug (void) +{ + return yy_flex_debug; +} + +void yyset_debug (int bdebug ) +{ + yy_flex_debug = bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = 0; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = (char *) 0; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = (FILE *) 0; + yyout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(); + } + + /* Destroy the stack itself. */ + yyfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +void *yyrealloc (void * ptr, yy_size_t size ) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void yyfree (void * ptr ) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 473 "config.l" + + + +int +yywrap(void) +{ + return pop_file(); /* no further input */ +} + +static int +push_file(char *name) +{ + file_stack_t *newfs; + file_stack_t *s; + FILE *newfile; + + if (yy_filename && !strcmp(name, yy_filename)) { + fprintf(stderr, "[conf] Warning: Attempted immediate circular inclusion of config file '%s'\n", + name); + return 1; + } + + s = file_stack; + while (s) { + if (!strcmp(name, s->name)) { + fprintf(stderr, "[conf] Warning: Attempted circular inclusion of config file '%s'\n", + name); + return 1; + } + s = s->next; + } + + if (!(newfile = fopen(name, "r"))) { + fprintf(stderr, "[conf] Warning: Could not open configuration file '%s'\n", name); + return 1; + } + + if (yyin) { + newfs = (struct _file_stack *) malloc(sizeof(struct _file_stack)); + newfs->handle = yy_fsci_active_buffer; + newfs->name = yy_filename; + + newfs->next = file_stack; + file_stack = newfs; + } + + yy_filename = strdup(name); + yy_fsci_active_buffer = yy_create_buffer(newfile,YY_BUF_SIZE); + yy_switch_to_buffer(yy_fsci_active_buffer); + + return 0; +} + +static int +pop_file(void) +{ + if (file_stack) { + void *goner = file_stack; + yy_delete_buffer(yy_fsci_active_buffer); + fclose(yyin); + yy_fsci_active_buffer = file_stack->handle; + yy_switch_to_buffer(yy_fsci_active_buffer); + + free(yy_filename); + yy_filename = file_stack->name; + file_stack = file_stack->next; + + free(goner); + return 0; + } else { + if (yy_filename) { + free(yy_filename); + yy_filename = NULL; + } + if (yyin) { + yy_delete_buffer(yy_fsci_active_buffer); + fclose(yyin); + yyin = NULL; + } + return 1; /* Done */ + } +} + + +char * +crop_value(char *yytext) +{ + char *retval; + + while (isspace(*yytext)) + ++yytext; + + retval = yytext; + if (*yytext == '"') { /* Magic Quote Mode */ + ++retval; + ++yytext; + while (*yytext && (*yytext != '"')) + ++yytext; + *yytext = 0; /* Terminate */ + } else { + while (*yytext && !isspace(*yytext)) + ++yytext; + *yytext = 0; /* Terminate */ + } + + return retval; +} + + +int +config_init(config_entry_t **_conf, char *conffile) +{ + char *homedir = sci_get_homedir(); + char *conf_path; + int i; + + conf = (config_entry_t *) sci_malloc(sizeof(config_entry_t)); +#ifdef SATISFY_PURIFY + memset(conf, 0, sizeof(config_entry_t)); +#endif + +/**** Default config: */ + conf->gfx_options.workarounds = 0; + conf->gfx_options.buffer_pics_nr = 0; + conf->gfx_options.correct_rendering = 1; + conf->gfx_options.pic0_unscaled = 1; + conf->gfx_options.pic0_dither_mode = GFXR_DITHER_MODE_D256; + conf->gfx_options.pic0_dither_pattern = GFXR_DITHER_PATTERN_SCALED; + conf->gfx_options.pic0_brush_mode = GFX_BRUSH_MODE_RANDOM_ELLIPSES; + conf->gfx_options.pic0_line_mode = GFX_LINE_MODE_CORRECT; + conf->gfx_options.cursor_xlate_filter = GFX_XLATE_FILTER_NONE; + conf->gfx_options.view_xlate_filter = GFX_XLATE_FILTER_NONE; + conf->gfx_options.pic_xlate_filter = GFX_XLATE_FILTER_NONE; + conf->gfx_options.text_xlate_filter = GFX_XLATE_FILTER_NONE; + conf->gfx_options.dirty_frames = GFXOP_DIRTY_FRAMES_CLUSTERS; + conf->gfx_options.pic0_antialiasing = GFXR_ANTIALIASING_NONE; + conf->gfx_options.pic_port_bounds = gfx_rect(0,10,320,190); + for (i = 0; i < GFX_RESOURCE_TYPES_NR; i++) { + conf->gfx_options.res_conf.assign[i] = NULL; + conf->gfx_options.res_conf.mod[i] = NULL; + } + + conf->gfx_driver_name = NULL; + +#ifdef __GNUC__ +#warning "Re-enable sound stuff" +#endif +#if 0 + conf->pcmout_driver = pcmout_find_driver(NULL); + conf->pcmout_rate = 22050; + conf->pcmout_stereo = 1; + conf->midiout_driver = midiout_find_driver(NULL); + conf->midi_device = midi_find_device(NULL); + conf->sound_server = sound_server_find_driver(NULL); +#endif + + conf->x_scale = 0; + conf->y_scale = 0; + conf->scale = 0; + conf->color_depth = 0; + + conf->mouse = 1; + conf->reverse_stereo = 0; + + conf->version = 0; + + conf->alpha_threshold = 0x90; + conf->animation_delay = 5; + conf->animation_granularity = 4; + conf->console_log = NULL; + conf->debug_mode [0] = '\0'; + conf->name = NULL; + conf->resource_dir = NULL; + conf->module_path = sci_strdup(SCI_DEFAULT_MODULE_PATH); + conf->res_version = SCI_VERSION_AUTODETECT; + + if (homedir) { + conf->menu_dir = (char*)sci_malloc(strlen(homedir) + strlen(FREESCI_GAMEDIR) + + strlen(FREESCI_GAMES_DIR) + 2 * strlen(G_DIR_SEPARATOR_S) + 1); + strcpy(conf->menu_dir, homedir); + strcat(conf->menu_dir, G_DIR_SEPARATOR_S); + strcat(conf->menu_dir, FREESCI_GAMEDIR); + strcat(conf->menu_dir, G_DIR_SEPARATOR_S); + strcat(conf->menu_dir, FREESCI_GAMES_DIR); + } + else + conf->menu_dir = NULL; + + for (i = 0; i < FREESCI_DRIVER_SUBSYSTEMS_NR; i++) + conf->driver_options[i] = NULL; +/**** Default config ends */ + + + if (conffile) { + exported_conf_path = (char *) sci_malloc(PATH_MAX + 1); + getcwd(exported_conf_path, PATH_MAX+1); + + conf_path = sci_strdup(conffile); /* Use config file if supplied */ + } else { + if (!homedir) { /* We're probably not under UNIX if this happens */ + + conf_path = sci_strdup(FREESCI_CONFFILE_DOS); /* Use DOS config style */ + + exported_conf_path = (char *) sci_malloc(PATH_MAX + 1); + getcwd(exported_conf_path, PATH_MAX+1); + + dospath = 1; /* Use DOS-style paths */ + + } else { + + /* So we've got a home directory */ + if (chdir(homedir)) { + fprintf(stderr,"Warning: Could not enter home directory: %s\n", homedir); + *_conf = conf; /* Set the result variable */ + return 1; + } + + if (chdir(FREESCI_GAMEDIR)) + if (scimkdir(FREESCI_GAMEDIR, 0700)) { + + fprintf(stderr,"Warning: Could not enter/create ~/"FREESCI_GAMEDIR"\n"); + *_conf = conf; /* Set the result variable */ + return 1; + } + + conf_path = (char *) sci_malloc(strlen(homedir) + 3 + strlen(FREESCI_GAMEDIR) + strlen(FREESCI_CONFFILE)); + strcpy(conf_path, homedir); + strcat(conf_path, "/"); + strcat(conf_path, FREESCI_GAMEDIR); + + exported_conf_path = sci_strdup(conf_path); + + strcat(conf_path, "/"); + strcat(conf_path, FREESCI_CONFFILE); + + dospath = 0; /* Use UN*X-style paths */ + } + } /* !conffile */ + + + if ((push_file(conf_path))) { + printf("No configuration file found; using defaults.\n"); + *_conf = conf; /* Set the result variable */ + sci_free(conf_path); + sci_free(exported_conf_path); + return 1; + } + + printf("Reading configuration...\n"); + + yylex(); /* Parse the file */ + + while (!pop_file()); /* Ignore error conditions- might be lex implementation dependant */ + sci_free(conf_path); + sci_free(exported_conf_path); + + *_conf = conf; /* Store the result */ + return cur_section + 1; +} + + +static void +config_free_driver_options(driver_option_t *option) +{ + if (option) { + sci_free(option->option); + sci_free(option->value); + + config_free_driver_options(option->next); + sci_free(option); + } +} + +static void +config_free_driver_subsystem(subsystem_options_t *subsys) +{ + if (subsys) { + sci_free(subsys->name); + + config_free_driver_options(subsys->options); + + config_free_driver_subsystem(subsys->next); + sci_free(subsys); + } +} + + +void +config_free(config_entry_t **conf, int entries) +{ + int i; + + if ((*conf)->console_log) + sci_free((*conf)->console_log); + + if ((*conf)->module_path) + sci_free((*conf)->module_path); + + if ((*conf)->menu_dir) + sci_free((*conf)->menu_dir); + + for (i = 0; i < entries; i++) { + int j; + + if (i >= 1) { + sci_free((*conf)[i].name); + if ((*conf)[i].resource_dir) + sci_free((*conf)[i].resource_dir); + if ((*conf)[i].console_log) + sci_free((*conf)[i].console_log); + } + + for (j = 0; j < FREESCI_DRIVER_SUBSYSTEMS_NR; j++) { + if ((*conf)[i].driver_options[j]) + config_free_driver_subsystem((*conf)[i].driver_options[j]); + } + } + + sci_free(*conf); +} + + +static int +parse_name(char *name, name_value_pair *nvps, const char *what, int oldval) +{ + int i = 0; + + while (nvps[i].name) { + if (0 == strcasecmp(name, nvps[i].name)) + return nvps[i].value; + + i++; + } + + printf("Invalid %s mode %s\n", what, name); + + return oldval; +} + + +#ifdef __GNUC__ +#warning "Re-enable sound stuff" +#endif +#if 0 +void * +parse_sound_server(char *driver_name) +{ + sound_server_t *retval = sound_server_find_driver(driver_name); + + if (retval) + return (void *) retval; + /* not found - return default */ + + printf ("Unknown sound server %s\n", driver_name); + return (void *) conf->sound_server; +} + +void * +parse_midiout_driver(char *driver_name) +{ + midiout_driver_t *retval = midiout_find_driver(driver_name); + + if (retval) + return (void *) retval; + /* not found - return default */ + + printf ("Unknown midiout driver %s\n", driver_name); + return (void *) conf->midiout_driver; +} + + +void * +parse_midi_device(char *driver_name) +{ + midi_device_t *retval = midi_find_device(driver_name); + + if (retval) + return (void *) retval; + /* not found - return default */ + + printf ("Unknown MIDI device %s\n", driver_name); + return (void *) conf->midi_device; +} + +void * +parse_pcmout_driver(char *driver_name) +{ + pcmout_driver_t *retval = pcmout_find_driver(driver_name); + + if (retval) + return (void *) retval; + /* not found - return default */ + + printf ("Unknown pcmout driver %s\n", driver_name); + return (void *) conf->pcmout_driver; +} +#endif + +static void +parse_option(char *option, int optlen, char *value) +{ + int optindex = 0; + standard_option *opt = NULL; + + while (!opt && standard_options[optindex].type) + if (optlen == strlen(standard_options[optindex].name) + && !strncmp(standard_options[optindex].name, option, optlen)) + opt = &(standard_options[optindex]); + else + optindex++; + + if (!opt) { + fprintf(stderr,"Invalid option '%s'\n", option); + return; + } + + switch (opt->type) { + + case OPTION_TYPE_INT: { + char *foo; + int int_value = strtol(value, &foo, 0); + + if (*foo) { + fprintf(stderr, "Option '%s' expects numeric value; encountered '%s'\n", + opt->name, value); + return; + } + + if (int_value < opt->min) { + fprintf(stderr, "Option '%s' expects value >= %d; encountered '%s'\n", opt->name, opt->min, value); + return; + } + + if (int_value > opt->max) { + fprintf(stderr, "Option '%s' expects value <= %d; encountered '%s'\n", opt->name, opt->max, value); + return; + } + + *((int *)(((char *)&(conf[cur_section])) + opt->varoffset)) = int_value; /* Store value */ + + break; + } + + case OPTION_TYPE_STRING: { + char **stringref = ((char **)(((char *)&(conf[cur_section])) + opt->varoffset)); + if (*stringref) + sci_free(*stringref); + *stringref = sci_strdup(value); /* Store value */ + break; + } + + case OPTION_TYPE_INVERSE_NVP: + case OPTION_TYPE_NVP: { + int int_value = parse_name(value, opt->nvp, opt->name, BAD_INT_VALUE); + + if (int_value != BAD_INT_VALUE) { + + if (opt->type == OPTION_TYPE_INVERSE_NVP) + int_value = !int_value; + +/* FUCKED HERE: cur_section = 0, opt->varoffset = 205 */ + *((int *)(((char*)&(conf[cur_section])) + opt->varoffset)) = int_value; /* Store value */ + } + break; + } + + + case OPTION_TYPE_RECT: { + char *seeker = value; + + /* A rect_t is four integers */ + int *result = (int *) ((char *) &(conf[cur_section]) + opt->varoffset); + int i; + + for (i=0;i<4;i++) + { + *(result++) = strtol(seeker, &seeker, 10); + if (i < 3) + { + while (((*seeker == ',') || (*seeker == ' ')) && + (*seeker != 0)) + seeker++; + if ((*seeker < '0') || (*seeker > '9')) + { + fprintf(stderr, "Option '%s' expects a rectangle\n", opt->name); + return; + } + } + } + + break; + } + + + case OPTION_TYPE_STATICREF: { + *((void **)(((char *)&(conf[cur_section])) + opt->varoffset)) = opt->parse_funct(value); + break; + } + + default: + fprintf(stderr, "INTERNAL ERROR in %s, parse_option(), line %d\n", __FILE__, __LINE__); + } +} + + +driver_option_t * +get_driver_options(config_entry_t *config, int subsystem, const char *name) +{ + subsystem_options_t *options; + + if (subsystem < 0 || subsystem >= FREESCI_DRIVER_SUBSYSTEMS_NR) { + fprintf(stderr, "Attempt to get options from invalid subsystem #%d!\n", subsystem); + return NULL; + } + + if (!config) + return NULL; + + + options = config->driver_options[subsystem]; + + while (options && strcasecmp(options->name, name)) + options = options->next; + + if (options) + return options->options; + + return NULL; +} + +static driver_option_t * +clone_driver_options(driver_option_t *options) +{ + driver_option_t *retval; + + if (!options) + return NULL; + + retval = (driver_option_t *) sci_malloc(sizeof(driver_option_t)); + retval->option = sci_strdup(options->option); + retval->value = sci_strdup(options->value); + retval->next = clone_driver_options(options->next); + + return retval; +} + +static subsystem_options_t * +clone_subsystem_options(subsystem_options_t *options) +{ + subsystem_options_t *retval; + + if (!options) + return NULL; + + retval = (subsystem_options_t *) sci_malloc(sizeof(subsystem_options_t)); + retval->name = sci_strdup(options->name); + retval->options = clone_driver_options(options->options); + retval->next = clone_subsystem_options(options->next); + + return retval; +} + +static void +copy_subsystem_options(config_entry_t *dest, config_entry_t *src) +{ + int i; + for (i = 0; i < FREESCI_DRIVER_SUBSYSTEMS_NR; i++) + dest->driver_options[i] = clone_subsystem_options(src->driver_options[i]); +} + + +char * +purge_comments(char *comments) +{ + char *c = comments; + int overwrite = 0; + char ch; + + /* Tear out all comments */ + while ((ch = *c)) { + if (ch == '#') + overwrite = 1; + if (ch == '\n') + overwrite = 0; + if (overwrite) + *c = ' '; + + c++; + } + + return comments; +} + +static void +set_config_parameter(config_entry_t *conf, char *subsystem_name, char *driver_name, + char *option, char *value) +{ + subsystem_options_t **subsys_optionsp; + driver_option_t **driver_optionsp; + int subsystem_nr = -1; + int i = 0; + + while (subsystem_nr == -1 && i < FREESCI_DRIVER_SUBSYSTEMS_NR) { + if (!strcasecmp(subsystem_name, freesci_subsystems[i].name)) + subsystem_nr = i; + i++; + } + + if (subsystem_nr == -1) { + sciprintf("config file: There is no subsystem named '%s'\n", subsystem_name); + return; + } + +#if 0 + if (!(freesci_subsystems[subsystem_nr].check_driver(driver_name))) { + sciprintf("config file: There is no %s driver called '%s'\n", subsystem_name, driver_name); + return; + } +#endif + + subsys_optionsp = &(conf->driver_options[subsystem_nr]); + + while (*subsys_optionsp && strcasecmp((*subsys_optionsp)->name, driver_name)) + subsys_optionsp = &((*subsys_optionsp)->next); + + if (!*subsys_optionsp) { + *subsys_optionsp = (subsystem_options_t *) sci_malloc(sizeof(subsystem_options_t)); + (*subsys_optionsp)->name = sci_strdup(driver_name); + (*subsys_optionsp)->next = NULL; + (*subsys_optionsp)->options = NULL; + } + + driver_optionsp = &((*subsys_optionsp)->options); + + while (*driver_optionsp && strcasecmp((*driver_optionsp)->option, option)) + driver_optionsp = &((*driver_optionsp)->next); + + if (*driver_optionsp) { + sci_free((*driver_optionsp)->value); + } else { + *driver_optionsp = (driver_option_t *) sci_malloc(sizeof(driver_option_t)); + (*driver_optionsp)->option = sci_strdup(option); + (*driver_optionsp)->next = NULL; + } + + (*driver_optionsp)->value = sci_strdup(value); +} + diff --git a/engines/sci/config.l b/engines/sci/config.l new file mode 100644 index 0000000000..7d05e43768 --- /dev/null +++ b/engines/sci/config.l @@ -0,0 +1,1116 @@ +/*************************************************************************** + config.l (C) 1999 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + +%{ +#include +#include +#include +#include + +/* unistd override for GNU flex for non-UNIX systems */ +#ifndef HAVE_UNISTD_H +# define YY_NO_UNISTD_H +#endif + +#ifdef _MSC_VER +# include +# include +#endif + +config_entry_t *conf; +int cur_section=0; /* Size-1 and current section in conf */ +char *exported_conf_path; /* Path which the config file was found in */ +int dospath; /* Use dos-style paths? */ + +typedef struct { + const char *name; + int value; +} name_value_pair; + + +struct { + const char *name; + void *(*check_driver)(char *name); +} freesci_subsystems[FREESCI_DRIVER_SUBSYSTEMS_NR] = { + {"gfx", NULL}, +#ifdef __GNUC__ +#warning "sound" +#endif +#if 0 + {"pcm", parse_pcmout_driver}, + {"midiout", parse_midiout_driver} +#endif +}; + + +typedef struct _file_stack { + char *name; + YY_BUFFER_STATE handle; + struct _file_stack *next; +} file_stack_t; + +static file_stack_t *file_stack = NULL; +static char *yy_filename = NULL; +static YY_BUFFER_STATE yy_fsci_active_buffer; + +static int +push_file(char *name); + +static int +pop_file(void); + +FILE *initial_file; + +static void +set_config_parameter(config_entry_t *conf, char *subsystem_name, char *driver_name, + char *option, char *value); + + +static int +parse_name(char *name, name_value_pair* nvps, const char *what, int oldval); /* Parses a string with a name value pair */ + +static void +copy_subsystem_options(config_entry_t *dest, config_entry_t *src); +/* Copies all subsystem options +** Parameters: (config_entry_t *) dest: The destination config struct +** (config_entry_t *) src: Source struct +** Returns : (void) +*/ + + +static name_value_pair valid_modes[] = { + {"default", GFXR_DITHER_MODE_D16}, + {"dither", GFXR_DITHER_MODE_D16}, + {"dither16", GFXR_DITHER_MODE_D16}, + {"dither_16", GFXR_DITHER_MODE_D16}, + {"dither-16", GFXR_DITHER_MODE_D16}, + {"d16", GFXR_DITHER_MODE_D16}, + {"flat", GFXR_DITHER_MODE_F256}, + {"interpol", GFXR_DITHER_MODE_F256}, + {"interpolate", GFXR_DITHER_MODE_F256}, + {"dither256", GFXR_DITHER_MODE_D256}, + {"dither_256", GFXR_DITHER_MODE_D256}, + {"d256", GFXR_DITHER_MODE_D256}, + {0, 0} /* Terminator */ +}; + +static name_value_pair yesno[] = { + {"yes", 1}, + {"no", 0}, + {"true", 1}, + {"false", 0}, + {"1", 1}, + {"0", 0}, + {"ok", 1}, + {"enable", 1}, + {"disable", 0}, + {"activate", 1}, + {"deactivate", 0}, + {"+", 1}, + {"-", 0}, + {"on", 1}, + {"off", 0}, + {0, 0} +}; + +static name_value_pair dither_pattern[] = { + {"scaled", GFXR_DITHER_PATTERN_SCALED}, + {"unscaled", GFXR_DITHER_PATTERN_1}, + {"one", GFXR_DITHER_PATTERN_1}, + {"1", GFXR_DITHER_PATTERN_1}, + {0, 0} +}; + +static name_value_pair dirty_strategy[] = { + {"1", GFXOP_DIRTY_FRAMES_ONE}, + {"one", GFXOP_DIRTY_FRAMES_ONE}, + {"cluster", GFXOP_DIRTY_FRAMES_CLUSTERS}, + {"clusters", GFXOP_DIRTY_FRAMES_CLUSTERS}, + {0, 0} +}; + +static name_value_pair brush_mode[] = { + {"scaled", GFX_BRUSH_MODE_SCALED}, + {"ellipse", GFX_BRUSH_MODE_ELLIPSES}, + {"ellipses", GFX_BRUSH_MODE_ELLIPSES}, + {"rnd_ellipses", GFX_BRUSH_MODE_RANDOM_ELLIPSES}, + {"rnd-ellipses", GFX_BRUSH_MODE_RANDOM_ELLIPSES}, + {"random_ellipses", GFX_BRUSH_MODE_RANDOM_ELLIPSES}, + {"random-ellipses", GFX_BRUSH_MODE_RANDOM_ELLIPSES}, + {"morerandom", GFX_BRUSH_MODE_MORERANDOM}, + {"more-random", GFX_BRUSH_MODE_MORERANDOM}, + {"more_random", GFX_BRUSH_MODE_MORERANDOM}, + {0, 0} +}; + +static name_value_pair filter_mode[] = { + {"none", GFX_XLATE_FILTER_NONE}, + {"linear", GFX_XLATE_FILTER_LINEAR}, + {"bilinear", GFX_XLATE_FILTER_LINEAR}, + {"bi-linear", GFX_XLATE_FILTER_LINEAR}, + {"trilinear", GFX_XLATE_FILTER_TRILINEAR}, + {"tri-linear", GFX_XLATE_FILTER_TRILINEAR}, + {0, 0} +}; + +static name_value_pair antialiasing_modes[] = { + {"none", GFXR_ANTIALIASING_NONE}, + {"0", GFXR_ANTIALIASING_NONE}, + {"off", GFXR_ANTIALIASING_NONE}, + {"on", GFXR_ANTIALIASING_SIMPLE}, + {"basic", GFXR_ANTIALIASING_SIMPLE}, + {"simple", GFXR_ANTIALIASING_SIMPLE} +}; + +static name_value_pair line_mode[] = { + {"correct", GFX_LINE_MODE_CORRECT}, + {"normal", GFX_LINE_MODE_CORRECT}, + {"fast", GFX_LINE_MODE_FAST}, + {"half", GFX_LINE_MODE_FAST}, + {"fine", GFX_LINE_MODE_FINE}, + {"thin", GFX_LINE_MODE_FINE}, + {0, 0} +}; + +#define BAD_INT_VALUE -33333333 + +/* Types of options */ +#define OPTION_TYPE_NONE 0 +#define OPTION_TYPE_INT 1 +#define OPTION_TYPE_NVP 2 +#define OPTION_TYPE_INVERSE_NVP 3 +#define OPTION_TYPE_STATICREF 4 +#define OPTION_TYPE_STRING 5 +#define OPTION_TYPE_RECT 6 + +typedef struct { + int type; + const char *name; + int min; + int max; + name_value_pair *nvp; + void * (*parse_funct)(char *); + int varoffset; +} standard_option; + +#define OPT_END {OPTION_TYPE_NONE, NULL, 0, 0, NULL, 0} +/* Terminates */ + +#define OPT_INT(NAME, VARNAME, MIN, MAX) {OPTION_TYPE_INT, NAME, MIN, MAX, NULL, NULL, offsetof (config_entry_t, VARNAME)} +/* Read INT from the interval [MIN, MAX] */ + +#define OPT_STRING(NAME, VARNAME) {OPTION_TYPE_STRING, NAME, 0, 0, NULL, NULL, offsetof (config_entry_t, VARNAME)} +/* Read a string */ + +#define OPT_NVP(NAME, VARNAME, NVP) {OPTION_TYPE_NVP, NAME, 0, 0, NVP, NULL, offsetof (config_entry_t, VARNAME)} +/* Read (name,value) pair from NVP */ + +#define OPT_INVERSE_NVP(NAME, VARNAME, NVP) {OPTION_TYPE_INVERSE_NVP, NAME, 0, 0, NVP, NULL, offsetof (config_entry_t, VARNAME)} +/* Read NVP and negate result */ + +#define OPT_STATICREF(NAME, VARNAME, FUNCTN) {OPTION_TYPE_STATICREF, NAME, 0, 0, NULL, FUNCTN, offsetof (config_entry_t, VARNAME)} +/* Call FUNCTN() with the specified value, store resulting NULL* */ + +#define OPT_RECT(NAME, VARNAME) {OPTION_TYPE_RECT, NAME, 0, 0, NULL, NULL, offsetof (config_entry_t, VARNAME)} +/* Read a rectangle */ + + +standard_option standard_options[] = { + OPT_RECT("pic_port_bounds", gfx_options.pic_port_bounds), + OPT_NVP("pic0_dither_mode", gfx_options.pic0_dither_mode, valid_modes), + OPT_NVP("color_mode", gfx_options.pic0_dither_mode, valid_modes), + OPT_NVP("pic0_dither_pattern", gfx_options.pic0_dither_pattern, dither_pattern), + OPT_NVP("pic0_brush_mode", gfx_options.pic0_brush_mode, brush_mode), + OPT_NVP("pic0_line_mode", gfx_options.pic0_line_mode, line_mode), + OPT_NVP("dirty_strategy", gfx_options.dirty_frames, dirty_strategy), + OPT_INVERSE_NVP("pic0_scaled", gfx_options.pic0_unscaled, yesno), + OPT_NVP("pic_filter", gfx_options.pic_xlate_filter, filter_mode), + OPT_NVP("pic_antialiasing", gfx_options.pic0_antialiasing, antialiasing_modes), + OPT_NVP("text_filter", gfx_options.text_xlate_filter, filter_mode), + OPT_NVP("view_filter", gfx_options.view_xlate_filter, filter_mode), + OPT_NVP("cursor_filter", gfx_options.cursor_xlate_filter, filter_mode), + OPT_NVP("mouse", mouse, yesno), + OPT_NVP("reverse_stereo", reverse_stereo, yesno), + OPT_INT("pic_buffer_size", gfx_options.buffer_pics_nr, 0, 4096), + OPT_INT("alpha_threshold", alpha_threshold, 0, 255), + OPT_INT("animation_delay", animation_delay, 0, 1000000), + OPT_INT("animation_granularity", animation_granularity, 1, 160), +#ifdef __GNUC__ +#warning "Re-enable sound server config" +#endif +#if 0 + OPT_STATICREF("midiout_driver", midiout_driver, parse_midiout_driver), + OPT_STATICREF("midi_device", midi_device, parse_midi_device), + OPT_STATICREF("sound_server", sound_server, parse_sound_server), + OPT_STATICREF("pcmout_driver", pcmout_driver, parse_pcmout_driver), + OPT_INT("pcmout_rate", pcmout_rate, 11025, 48000), + OPT_INT("pcmout_stereo", pcmout_stereo, 0, 1), +#endif + OPT_STRING("console_log", console_log), + OPT_STRING("module_path", module_path), + OPT_STRING("menu_dir", menu_dir), + OPT_STRING("gfx_driver", gfx_driver_name), + OPT_INT("scale_x", x_scale, 1, 256), + OPT_INT("scale_y", y_scale, 1, 256), + OPT_INT("scale", scale, 1, 256), + OPT_INT("resource_version", res_version, 0, 6), + OPT_INT("color_depth", color_depth, 8, 32), + OPT_END +}; + + +static void +parse_option(char *option, int optlen, char *value); + +char * +crop_value(char *yytext); + +char * +purge_comments(char *comments); + +%} + +DIGIT [0-9] +PARAMTOKEN [[:alnum:]"."_\:\/-]* +SCIVERSION {DIGIT}"."{DIGIT}{3}"."{DIGIT}{3} +NUMTOKEN {DIGIT}+ +COORDTOKEN {NUMTOKEN},?[[:space:]]* +RECTTOKEN {COORDTOKEN}{3}{NUMTOKEN} +NUMPARAMTOKEN {NUMTOKEN}|{PARAMTOKEN} +PATHTOKEN [[:alnum:]"/""\\""."]* +NUMPATHPARAMTOKEN {NUMPARAMTOKEN}|{PATHTOKEN} +QUOTED_NUMPARAMTOKEN "\"".*"\"" + +%% + +"["[_[:alnum:]]+"]" { + char *cleanup; + ++yytext; /* Get over opening bracket */ + + ++cur_section; /* Start new section */ + + /* Create new entry... */ + conf = (config_entry_t *) sci_realloc(conf, sizeof(config_entry_t) * (cur_section + 1)); + + /* ...and initialize it */ + memcpy(&(conf[cur_section]), &(conf[0]), sizeof(config_entry_t)); + if (conf[0].console_log) + conf[cur_section].console_log = sci_strdup (conf[0].console_log); + + /* Copy the subsystem init strings */ + copy_subsystem_options(conf + cur_section, conf); + + while (isspace(*yytext)) + yytext++; + + cleanup = strchr(yytext, ']'); + + do { + *cleanup-- = 0; + } while (isblank(*cleanup)); + + conf[cur_section].name = sci_strdup(yytext); + + conf[cur_section].resource_dir = sci_strdup("."); + +} + + +version[[:blank:]]*"="[[:blank:]]*{SCIVERSION} { + + yytext = strchr(yytext, '=') + 1; + + while (isspace(*yytext)) + yytext++; + + version_parse(yytext, &conf[cur_section].version); +} + +resource_dir[[:blank:]]*"="[[:blank:]]*.+ if (cur_section) { + yytext = strchr(yytext, '=') + 1; + while (isspace(*yytext)) + yytext++; + + sci_free(conf[cur_section].resource_dir); + + conf[cur_section].resource_dir = sci_strdup(yytext); +} + +debug_mode[[:blank:]]*"="[[:blank:]]*.+ { + yytext = strchr(yytext, '=') + 1; + + while (isspace(*yytext)) + yytext++; + + strcpy (conf[cur_section].debug_mode, yytext); +} + +[[:alnum:]]+"."[[:alnum:]]+"."[[:alnum:]_]+[[:blank:]]*"="[[:blank:]]*({NUMPARAMTOKEN}|{QUOTED_NUMPARAMTOKEN})[[:blank:]]* { +/* driver parameters */ + char *subsys_name = yytext; + char *driver_name; + char *option, *value; + char *p2; + + yytext = strchr(yytext, '.'); + *yytext++ = 0; + driver_name = yytext; + yytext = strchr(yytext, '.'); + *yytext++ = 0; + + + option = yytext; + yytext = strchr(yytext, '='); + *yytext++ = 0; + + p2 = yytext-2; /* trim right spaces */ + while (p2 > option && isspace (*p2)) + *p2-- = 0; + + value = crop_value(yytext); /* Get config value */ + + set_config_parameter(conf + cur_section, subsys_name, + driver_name, option, value); + +} + + +{PARAMTOKEN}[[:blank:]]*"="[[:blank:]]*({NUMPATHPARAMTOKEN}|{QUOTED_NUMPARAMTOKEN})[[:blank:]]* { /* Normal config option */ + char *option_str = yytext; + char *value_str = yytext; + int option_str_len; + + while (isalnum(*value_str) || *value_str == '_') + ++value_str; + + option_str_len = value_str - option_str; + + while (!(isalnum(*value_str) || *value_str == '_' || *value_str == '"' || *value_str == '/' || *value_str == '\\')) + ++value_str; + + value_str = crop_value(value_str); + + parse_option(option_str, option_str_len, value_str); +} + +{PARAMTOKEN}[[:blank:]]*"="[[:blank:]]*\"{RECTTOKEN}\" { /* Normal config option */ + char *option_str = yytext; + char *value_str = yytext; + int option_str_len; + + while (isalnum(*value_str) || *value_str == '_') + ++value_str; + + option_str_len = value_str - option_str; + + while (!(isdigit(*value_str)||(*value_str == '"'))) + ++value_str; + + value_str = crop_value(value_str); + + parse_option(option_str, option_str_len, value_str); +} + + +(view|pic|cursor)[^_A-Za-z0-9"("]*"("([^";"]|"#"[^\n]*\n)*";" { + gfx_update_conf(&(conf[cur_section].gfx_options), purge_comments(yytext)); +} + +"%include"[^<\n]*<[^>\n]*> { + char *filename = strchr(yytext, '<'); + char *end = strchr(filename, '>'); + + *end-- = 0; + while (isblank(*end)) + *end-- = 0; + + filename++; + while (*filename && isblank(*filename)) + filename++; + + push_file(filename); + YY_NEW_FILE; +} + + +"#".+$ /* Ignore comments */ + +[[:blank:]\n]+ /* Eat whitespace */ + +<> { + yy_delete_buffer( YY_CURRENT_BUFFER ); + yyterminate(); +} + +.* printf("Unrecognized option: '%s'\n", yytext); + +%% + +int +yywrap(void) +{ + return pop_file(); /* no further input */ +} + +static int +push_file(char *name) +{ + file_stack_t *newfs; + file_stack_t *s; + FILE *newfile; + + if (yy_filename && !strcmp(name, yy_filename)) { + fprintf(stderr, "[conf] Warning: Attempted immediate circular inclusion of config file '%s'\n", + name); + return 1; + } + + s = file_stack; + while (s) { + if (!strcmp(name, s->name)) { + fprintf(stderr, "[conf] Warning: Attempted circular inclusion of config file '%s'\n", + name); + return 1; + } + s = s->next; + } + + if (!(newfile = fopen(name, "r"))) { + fprintf(stderr, "[conf] Warning: Could not open configuration file '%s'\n", name); + return 1; + } + + if (yyin) { + newfs = (struct _file_stack *) malloc(sizeof(struct _file_stack)); + newfs->handle = yy_fsci_active_buffer; + newfs->name = yy_filename; + + newfs->next = file_stack; + file_stack = newfs; + } + + yy_filename = strdup(name); + yy_fsci_active_buffer = yy_create_buffer(newfile, YY_BUF_SIZE); + yy_switch_to_buffer(yy_fsci_active_buffer); + + return 0; +} + +static int +pop_file(void) +{ + if (file_stack) { + void *goner = file_stack; + yy_delete_buffer(yy_fsci_active_buffer); + fclose(yyin); + yy_fsci_active_buffer = file_stack->handle; + yy_switch_to_buffer(yy_fsci_active_buffer); + + free(yy_filename); + yy_filename = file_stack->name; + file_stack = file_stack->next; + + free(goner); + return 0; + } else { + if (yy_filename) { + free(yy_filename); + yy_filename = NULL; + } + if (yyin) { + yy_delete_buffer(yy_fsci_active_buffer); + fclose(yyin); + yyin = NULL; + } + return 1; /* Done */ + } +} + + +char * +crop_value(char *yytext) +{ + char *retval; + + while (isspace(*yytext)) + ++yytext; + + retval = yytext; + if (*yytext == '"') { /* Magic Quote Mode */ + ++retval; + ++yytext; + while (*yytext && (*yytext != '"')) + ++yytext; + *yytext = 0; /* Terminate */ + } else { + while (*yytext && !isspace(*yytext)) + ++yytext; + *yytext = 0; /* Terminate */ + } + + return retval; +} + + +int +config_init(config_entry_t **_conf, char *conffile) +{ + char *homedir = sci_get_homedir(); + char *conf_path; + int i; + + conf = (config_entry_t *) sci_malloc(sizeof(config_entry_t)); +#ifdef SATISFY_PURIFY + memset(conf, 0, sizeof(config_entry_t)); +#endif + +/**** Default config: */ + conf->gfx_options.workarounds = 0; + conf->gfx_options.buffer_pics_nr = 0; + conf->gfx_options.correct_rendering = 1; + conf->gfx_options.pic0_unscaled = 1; + conf->gfx_options.pic0_dither_mode = GFXR_DITHER_MODE_D256; + conf->gfx_options.pic0_dither_pattern = GFXR_DITHER_PATTERN_SCALED; + conf->gfx_options.pic0_brush_mode = GFX_BRUSH_MODE_RANDOM_ELLIPSES; + conf->gfx_options.pic0_line_mode = GFX_LINE_MODE_CORRECT; + conf->gfx_options.cursor_xlate_filter = GFX_XLATE_FILTER_NONE; + conf->gfx_options.view_xlate_filter = GFX_XLATE_FILTER_NONE; + conf->gfx_options.pic_xlate_filter = GFX_XLATE_FILTER_NONE; + conf->gfx_options.text_xlate_filter = GFX_XLATE_FILTER_NONE; + conf->gfx_options.dirty_frames = GFXOP_DIRTY_FRAMES_CLUSTERS; + conf->gfx_options.pic0_antialiasing = GFXR_ANTIALIASING_NONE; + conf->gfx_options.pic_port_bounds = gfx_rect(0,10,320,190); + for (i = 0; i < GFX_RESOURCE_TYPES_NR; i++) { + conf->gfx_options.res_conf.assign[i] = NULL; + conf->gfx_options.res_conf.mod[i] = NULL; + } + + conf->gfx_driver_name = NULL; + +#ifdef __GNUC__ +#warning "Re-enable sound stuff" +#endif +#if 0 + conf->pcmout_driver = pcmout_find_driver(NULL); + conf->pcmout_rate = 22050; + conf->pcmout_stereo = 1; + conf->midiout_driver = midiout_find_driver(NULL); + conf->midi_device = midi_find_device(NULL); + conf->sound_server = sound_server_find_driver(NULL); +#endif + + conf->x_scale = 0; + conf->y_scale = 0; + conf->scale = 0; + conf->color_depth = 0; + + conf->mouse = 1; + conf->reverse_stereo = 0; + + conf->version = 0; + + conf->alpha_threshold = 0x90; + conf->animation_delay = 5; + conf->animation_granularity = 4; + conf->console_log = NULL; + conf->debug_mode [0] = '\0'; + conf->name = NULL; + conf->resource_dir = NULL; + conf->module_path = sci_strdup(SCI_DEFAULT_MODULE_PATH); + conf->res_version = SCI_VERSION_AUTODETECT; + + if (homedir) { + conf->menu_dir = (char*)sci_malloc(strlen(homedir) + strlen(FREESCI_GAMEDIR) + + strlen(FREESCI_GAMES_DIR) + 2 * strlen(G_DIR_SEPARATOR_S) + 1); + strcpy(conf->menu_dir, homedir); + strcat(conf->menu_dir, G_DIR_SEPARATOR_S); + strcat(conf->menu_dir, FREESCI_GAMEDIR); + strcat(conf->menu_dir, G_DIR_SEPARATOR_S); + strcat(conf->menu_dir, FREESCI_GAMES_DIR); + } + else + conf->menu_dir = NULL; + + for (i = 0; i < FREESCI_DRIVER_SUBSYSTEMS_NR; i++) + conf->driver_options[i] = NULL; +/**** Default config ends */ + + + if (conffile) { + exported_conf_path = (char *) sci_malloc(PATH_MAX + 1); + getcwd(exported_conf_path, PATH_MAX+1); + + conf_path = sci_strdup(conffile); /* Use config file if supplied */ + } else { + if (!homedir) { /* We're probably not under UNIX if this happens */ + + conf_path = sci_strdup(FREESCI_CONFFILE_DOS); /* Use DOS config style */ + + exported_conf_path = (char *) sci_malloc(PATH_MAX + 1); + getcwd(exported_conf_path, PATH_MAX+1); + + dospath = 1; /* Use DOS-style paths */ + + } else { + + /* So we've got a home directory */ + if (chdir(homedir)) { + fprintf(stderr,"Warning: Could not enter home directory: %s\n", homedir); + *_conf = conf; /* Set the result variable */ + return 1; + } + + if (chdir(FREESCI_GAMEDIR)) + if (scimkdir(FREESCI_GAMEDIR, 0700)) { + + fprintf(stderr,"Warning: Could not enter/create ~/"FREESCI_GAMEDIR"\n"); + *_conf = conf; /* Set the result variable */ + return 1; + } + + conf_path = (char *) sci_malloc(strlen(homedir) + 3 + strlen(FREESCI_GAMEDIR) + strlen(FREESCI_CONFFILE)); + strcpy(conf_path, homedir); + strcat(conf_path, "/"); + strcat(conf_path, FREESCI_GAMEDIR); + + exported_conf_path = sci_strdup(conf_path); + + strcat(conf_path, "/"); + strcat(conf_path, FREESCI_CONFFILE); + + dospath = 0; /* Use UN*X-style paths */ + } + } /* !conffile */ + + + if ((push_file(conf_path))) { + printf("No configuration file found; using defaults.\n"); + *_conf = conf; /* Set the result variable */ + sci_free(conf_path); + sci_free(exported_conf_path); + return 1; + } + + printf("Reading configuration...\n"); + + yylex(); /* Parse the file */ + + while (!pop_file()); /* Ignore error conditions- might be lex implementation dependant */ + sci_free(conf_path); + sci_free(exported_conf_path); + + *_conf = conf; /* Store the result */ + return cur_section + 1; +} + + +static void +config_free_driver_options(driver_option_t *option) +{ + if (option) { + sci_free(option->option); + sci_free(option->value); + + config_free_driver_options(option->next); + sci_free(option); + } +} + +static void +config_free_driver_subsystem(subsystem_options_t *subsys) +{ + if (subsys) { + sci_free(subsys->name); + + config_free_driver_options(subsys->options); + + config_free_driver_subsystem(subsys->next); + sci_free(subsys); + } +} + + +void +config_free(config_entry_t **conf, int entries) +{ + int i; + + if ((*conf)->console_log) + sci_free((*conf)->console_log); + + if ((*conf)->module_path) + sci_free((*conf)->module_path); + + if ((*conf)->menu_dir) + sci_free((*conf)->menu_dir); + + for (i = 0; i < entries; i++) { + int j; + + if (i >= 1) { + sci_free((*conf)[i].name); + if ((*conf)[i].resource_dir) + sci_free((*conf)[i].resource_dir); + if ((*conf)[i].console_log) + sci_free((*conf)[i].console_log); + } + + for (j = 0; j < FREESCI_DRIVER_SUBSYSTEMS_NR; j++) { + if ((*conf)[i].driver_options[j]) + config_free_driver_subsystem((*conf)[i].driver_options[j]); + } + } + + sci_free(*conf); +} + + +static int +parse_name(char *name, name_value_pair *nvps, const char *what, int oldval) +{ + int i = 0; + + while (nvps[i].name) { + if (0 == strcasecmp(name, nvps[i].name)) + return nvps[i].value; + + i++; + } + + printf("Invalid %s mode %s\n", what, name); + + return oldval; +} + + +#ifdef __GNUC__ +#warning "Re-enable sound stuff" +#endif +#if 0 +void * +parse_sound_server(char *driver_name) +{ + sound_server_t *retval = sound_server_find_driver(driver_name); + + if (retval) + return (void *) retval; + /* not found - return default */ + + printf ("Unknown sound server %s\n", driver_name); + return (void *) conf->sound_server; +} + +void * +parse_midiout_driver(char *driver_name) +{ + midiout_driver_t *retval = midiout_find_driver(driver_name); + + if (retval) + return (void *) retval; + /* not found - return default */ + + printf ("Unknown midiout driver %s\n", driver_name); + return (void *) conf->midiout_driver; +} + + +void * +parse_midi_device(char *driver_name) +{ + midi_device_t *retval = midi_find_device(driver_name); + + if (retval) + return (void *) retval; + /* not found - return default */ + + printf ("Unknown MIDI device %s\n", driver_name); + return (void *) conf->midi_device; +} + +void * +parse_pcmout_driver(char *driver_name) +{ + pcmout_driver_t *retval = pcmout_find_driver(driver_name); + + if (retval) + return (void *) retval; + /* not found - return default */ + + printf ("Unknown pcmout driver %s\n", driver_name); + return (void *) conf->pcmout_driver; +} +#endif + +static void +parse_option(char *option, int optlen, char *value) +{ + int optindex = 0; + standard_option *opt = NULL; + + while (!opt && standard_options[optindex].type) + if (optlen == strlen(standard_options[optindex].name) + && !strncmp(standard_options[optindex].name, option, optlen)) + opt = &(standard_options[optindex]); + else + optindex++; + + if (!opt) { + fprintf(stderr,"Invalid option '%s'\n", option); + return; + } + + switch (opt->type) { + + case OPTION_TYPE_INT: { + char *foo; + int int_value = strtol(value, &foo, 0); + + if (*foo) { + fprintf(stderr, "Option '%s' expects numeric value; encountered '%s'\n", + opt->name, value); + return; + } + + if (int_value < opt->min) { + fprintf(stderr, "Option '%s' expects value >= %d; encountered '%s'\n", opt->name, opt->min, value); + return; + } + + if (int_value > opt->max) { + fprintf(stderr, "Option '%s' expects value <= %d; encountered '%s'\n", opt->name, opt->max, value); + return; + } + + *((int *)(((char *)&(conf[cur_section])) + opt->varoffset)) = int_value; /* Store value */ + + break; + } + + case OPTION_TYPE_STRING: { + char **stringref = ((char **)(((char *)&(conf[cur_section])) + opt->varoffset)); + if (*stringref) + sci_free(*stringref); + *stringref = sci_strdup(value); /* Store value */ + break; + } + + case OPTION_TYPE_INVERSE_NVP: + case OPTION_TYPE_NVP: { + int int_value = parse_name(value, opt->nvp, opt->name, BAD_INT_VALUE); + + if (int_value != BAD_INT_VALUE) { + + if (opt->type == OPTION_TYPE_INVERSE_NVP) + int_value = !int_value; + +/* FUCKED HERE: cur_section = 0, opt->varoffset = 205 */ + *((int *)(((char*)&(conf[cur_section])) + opt->varoffset)) = int_value; /* Store value */ + } + break; + } + + + case OPTION_TYPE_RECT: { + char *seeker = value; + + /* A rect_t is four integers */ + int *result = (int *) ((char *) &(conf[cur_section]) + opt->varoffset); + int i; + + for (i=0;i<4;i++) + { + *(result++) = strtol(seeker, &seeker, 10); + if (i < 3) + { + while (((*seeker == ',') || (*seeker == ' ')) && + (*seeker != 0)) + seeker++; + if ((*seeker < '0') || (*seeker > '9')) + { + fprintf(stderr, "Option '%s' expects a rectangle\n", opt->name); + return; + } + } + } + + break; + } + + + case OPTION_TYPE_STATICREF: { + *((void **)(((char *)&(conf[cur_section])) + opt->varoffset)) = opt->parse_funct(value); + break; + } + + default: + fprintf(stderr, "INTERNAL ERROR in %s, parse_option(), line %d\n", __FILE__, __LINE__); + } +} + + +driver_option_t * +get_driver_options(config_entry_t *config, int subsystem, const char *name) +{ + subsystem_options_t *options; + + if (subsystem < 0 || subsystem >= FREESCI_DRIVER_SUBSYSTEMS_NR) { + fprintf(stderr, "Attempt to get options from invalid subsystem #%d!\n", subsystem); + return NULL; + } + + if (!config) + return NULL; + + + options = config->driver_options[subsystem]; + + while (options && strcasecmp(options->name, name)) + options = options->next; + + if (options) + return options->options; + + return NULL; +} + +static driver_option_t * +clone_driver_options(driver_option_t *options) +{ + driver_option_t *retval; + + if (!options) + return NULL; + + retval = (driver_option_t *) sci_malloc(sizeof(driver_option_t)); + retval->option = sci_strdup(options->option); + retval->value = sci_strdup(options->value); + retval->next = clone_driver_options(options->next); + + return retval; +} + +static subsystem_options_t * +clone_subsystem_options(subsystem_options_t *options) +{ + subsystem_options_t *retval; + + if (!options) + return NULL; + + retval = (subsystem_options_t *) sci_malloc(sizeof(subsystem_options_t)); + retval->name = sci_strdup(options->name); + retval->options = clone_driver_options(options->options); + retval->next = clone_subsystem_options(options->next); + + return retval; +} + +static void +copy_subsystem_options(config_entry_t *dest, config_entry_t *src) +{ + int i; + for (i = 0; i < FREESCI_DRIVER_SUBSYSTEMS_NR; i++) + dest->driver_options[i] = clone_subsystem_options(src->driver_options[i]); +} + + +char * +purge_comments(char *comments) +{ + char *c = comments; + int overwrite = 0; + char ch; + + /* Tear out all comments */ + while ((ch = *c)) { + if (ch == '#') + overwrite = 1; + if (ch == '\n') + overwrite = 0; + if (overwrite) + *c = ' '; + + c++; + } + + return comments; +} + +static void +set_config_parameter(config_entry_t *conf, char *subsystem_name, char *driver_name, + char *option, char *value) +{ + subsystem_options_t **subsys_optionsp; + driver_option_t **driver_optionsp; + int subsystem_nr = -1; + int i = 0; + + while (subsystem_nr == -1 && i < FREESCI_DRIVER_SUBSYSTEMS_NR) { + if (!strcasecmp(subsystem_name, freesci_subsystems[i].name)) + subsystem_nr = i; + i++; + } + + if (subsystem_nr == -1) { + sciprintf("config file: There is no subsystem named '%s'\n", subsystem_name); + return; + } + +#if 0 + if (!(freesci_subsystems[subsystem_nr].check_driver(driver_name))) { + sciprintf("config file: There is no %s driver called '%s'\n", subsystem_name, driver_name); + return; + } +#endif + + subsys_optionsp = &(conf->driver_options[subsystem_nr]); + + while (*subsys_optionsp && strcasecmp((*subsys_optionsp)->name, driver_name)) + subsys_optionsp = &((*subsys_optionsp)->next); + + if (!*subsys_optionsp) { + *subsys_optionsp = (subsystem_options_t *) sci_malloc(sizeof(subsystem_options_t)); + (*subsys_optionsp)->name = sci_strdup(driver_name); + (*subsys_optionsp)->next = NULL; + (*subsys_optionsp)->options = NULL; + } + + driver_optionsp = &((*subsys_optionsp)->options); + + while (*driver_optionsp && strcasecmp((*driver_optionsp)->option, option)) + driver_optionsp = &((*driver_optionsp)->next); + + if (*driver_optionsp) { + sci_free((*driver_optionsp)->value); + } else { + *driver_optionsp = (driver_option_t *) sci_malloc(sizeof(driver_option_t)); + (*driver_optionsp)->option = sci_strdup(option); + (*driver_optionsp)->next = NULL; + } + + (*driver_optionsp)->value = sci_strdup(value); +} diff --git a/engines/sci/config/Makefile.am b/engines/sci/config/Makefile.am new file mode 100644 index 0000000000..6659da6000 --- /dev/null +++ b/engines/sci/config/Makefile.am @@ -0,0 +1,13 @@ +INCLUDES = -I$(top_srcdir)/src/include + +noinst_LIBRARIES = libsciconfig.a +libsciconfig_a_SOURCES = config.l extension.c +EXTRA_DIST = lsl2.scifx sq3.scifx config.test + +check_PROGRAMS = test-parse +test_parse_LDADD = libsciconfig.a ../scicore/libscicore.a +test_parse_SOURCES = test-parse.c + +check-parse.sh : test-parse + +TESTS = check-parse.sh \ No newline at end of file diff --git a/engines/sci/config/config.l b/engines/sci/config/config.l new file mode 100644 index 0000000000..54890da43d --- /dev/null +++ b/engines/sci/config/config.l @@ -0,0 +1,702 @@ +/*************************************************************************** + config.l (C) 1999 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + +%{ +#include +#include +#include "conf_parse.h" +#include "conf_extension.h" +#include "sci_memory.h" +#include +#include + + +/* unistd override for GNU flex for non-UNIX systems */ +#ifndef HAVE_UNISTD_H +# define YY_NO_UNISTD_H +#endif + +#ifdef _MSC_VER +# include +# include + +# define strcasecmp stricmp + +#endif + +char * cur_section = NULL; /* dynamically allocated */ + +typedef struct _file_stack { + char *name; + int line_nr; /* Which line were we included _from_? */ + conf_parse_t *input_spec; /* The config_parse_t that included this file */ + YY_BUFFER_STATE handle; + struct _file_stack *next; +} file_stack_t; + +static file_stack_t *file_stack = NULL; +static char *yy_filename = NULL; +static YY_BUFFER_STATE yy_fsci_active_buffer; + +static int +push_file(char *name); + +static int +pop_file(void); + +FILE *initial_file; + +static const char * +skip_while(const char *start, int (*predicate)(int)); +/* Returns the first character after ``start'' which is 0 or for which ``predicate'' fails +** Parameters: (char *) start: Start position for the search +** (int -> int) predicate: Termination predicate +** Returns : (char *) The first character which is 0 or for which ``prediate'' fails +*/ + +static const char * +skip_until(const char *start, int (*predicate)(int)); +/* Returns the first character after ``start'' which is 0 or for which ``predicate'' succeeds +** Parameters: (char *) start: Start position for the search +** (int -> int) predicate: Termination predicate +** Returns : (char *) The first character which is 0 or for which ``prediate'' succeeds +*/ + +static char * +string_between (const char *start, const char *end); +/* Allocates a copy of the string between ``start'' and ``end'' (exluding ``end'') +** Parameters: (char *) start: Start position for copying +** (char *) end: End position for copying +*/ + +static int +is_separator_or_alnum(int character); +/* Determines whether ``character'' is an underscore, dash, period, or alphanumeric character +** Parameters: (int) character: The char to test +** Returns : (int) nonzero iff isalnum(character) or character equals '-' or '_' or ':' or '.' +*/ + +static int +is_double_quote(int character); +/* Determines whether ``character'' is '"' +** Parameters: (int) character: The char to test +** Returns : (int) nonzero iff character = '"' +*/ + +static int +is_period(int character); +/* Determines whether ``character'' is '.' +** Parameters: (int) character: The char to test +** Returns : (int) nonzero iff character = '.' +*/ + +static conf_parse_t * +next_conf_parse(int type); +/* Allocates a new conf_parse_t, tagged as being next in the input record +** Parameters: (int) type: The type of this section +** Returns : (conf_parse_t *) An appropriate conf parse entry +** Note that the subsection is implicitly chained in with the present subsection. +*/ + +static void +record_whitespace(const char *yytext); +/* Records an instance of whitespace +** Parameters: (char *) yytext: The whitespace to copy and represent +** Returns : (void) +*/ + +conf_extension_t * +conf_extension_alloc(int type); +/* Allocates a new chunk of configuration extension memory +** Parameters: (int) type: The type to pre-set +** Returns : (conf_extension_t *) : Fresh memory, with only the type initialised +*/ + + +static int +pop_file(void); + + +static conf_parse_t *current_confparse = NULL; +static conf_parse_t **current_confparse_start = NULL; /* Non-NULL if we request a pointer to the next current_confparse to be set; used for include files */ +static int conf_line_nr = 1; + +#define BAD_INT_VALUE -33333333 + +%} + +DIGIT [0-9] +PARAMTOKEN [[:alnum:]_\:\/-]* +SCIVERSION {DIGIT}"."{DIGIT}{3}"."{DIGIT}{3} +NUMTOKEN {DIGIT}+ +COORDTOKEN {NUMTOKEN},?[[:space:]]* +RECTTOKEN {COORDTOKEN}{3}{NUMTOKEN} +QUOTED_RECTTOKEN "\""{RECTTOKEN}"\"" +NUMPARAMTOKEN {NUMTOKEN}|{PARAMTOKEN} +PATHTOKEN [[:alnum:]"/""\\""."]* +NUMPATHPARAMTOKEN {NUMPARAMTOKEN}|{PATHTOKEN} +QUOTED_NUMPARAMTOKEN "\"".*"\"" + +%% + +"["[_[:alnum:]-]+"]" { + const char *start = yytext; + const char *textstart = skip_until(start+1, is_separator_or_alnum); + const char *textend = skip_while(textstart+1, is_separator_or_alnum); + + char *pre_pad = string_between(start, textstart); + char *section_name = string_between(textstart, textend); + char *post_pad = sci_strdup(textend); + + conf_parse_t *confdata = next_conf_parse(CONF_PARSE_TYPE_SUBSECTION); + confdata->data.subsection.pre_whitespace = pre_pad; + confdata->data.subsection.name = section_name; + confdata->data.subsection.post_whitespace = post_pad; +} + + +({PARAMTOKEN}|.)+[[:blank:]]*"="[[:blank:]]*({NUMPATHPARAMTOKEN}|{QUOTED_NUMPARAMTOKEN}|{RECTTOKEN}|{QUOTED_RECTTOKEN})[[:blank:]]* { + const char *name_start = yytext; + const char *name_end = skip_while(name_start + 1, is_separator_or_alnum); + + const char *name_interperiod_2 = NULL; + const char *name_interperiod_1 = skip_until(name_start + 1, is_period); + + if (*name_interperiod_1 == 0 + || name_interperiod_1 > name_end) + name_interperiod_1 = NULL; /* No separator */ + else { + name_interperiod_2 = skip_until(name_interperiod_1 + 1, is_period); + if (*name_interperiod_2 == 0) + name_interperiod_2 = NULL; /* No separator */ + } + + const char *value_start = skip_until(name_end + 1, is_separator_or_alnum); /* Note that '=' is not a separator or alnum */ + const char *value_end = is_double_quote(*(value_start - 1)) + ? skip_until(value_start + 1, is_double_quote) /* double quote: Look for terminator */ + : skip_while(value_start + 1, is_separator_or_alnum); /* no double quote: Look for end of ``identifier'' like thing */ + + char *subsystem_name = NULL; + char *driver_name = NULL; + char *option_name = NULL; + + if (name_interperiod_2) { + subsystem_name = string_between(name_start, name_interperiod_1); + driver_name = string_between(name_interperiod_1 + 1, name_interperiod_2); + option_name = string_between(name_interperiod_2 + 1, name_end); + } else if (name_interperiod_1) { + subsystem_name = string_between(name_start, name_interperiod_1); + option_name = string_between(name_interperiod_1 + 1, name_end); + } else + option_name = string_between(name_start, name_end); + + char *whitespace = string_between(name_end, value_start); + char *value = string_between(value_start, value_end); + fprintf(stderr, "value_end = %p\n", value_end); + char *terminal_whitespace = sci_strdup(value_end); + + conf_parse_t *confdata = next_conf_parse(CONF_PARSE_TYPE_OPTION); + confdata->data.assignment.subsystem = subsystem_name; + confdata->data.assignment.driver = driver_name; + confdata->data.assignment.option = option_name; + confdata->data.assignment.centre_whitespace = whitespace; + confdata->data.assignment.value = value; + confdata->data.assignment.terminal_whitespace = terminal_whitespace; +} + + +(view|pic|cursor)[^_A-Za-z0-9"("]*"("([^";"]|"#"[^\n]*\n)*";" { + + conf_parse_t *confdata = next_conf_parse(CONF_PARSE_TYPE_EXTENSION); + conf_extension_t *ext = conf_extension_alloc(CONF_EXT_TYPE_GFX); + ext->data = sci_strdup(yytext); + confdata->data.extension = ext; +} + +"%include"[^<\n]*<[^>\n]*> { + const char *file_start = strchr(yytext, '<') + 1; + const char *file_end = strchr(file_start, '>'); + + conf_parse_t *confdata = next_conf_parse(CONF_PARSE_TYPE_INCLUDE); + char *include_file = string_between(file_start, file_end); + confdata->data.include.include_prefix = string_between(yytext, file_start); + confdata->data.include.include_suffix = sci_strdup(file_end); + confdata->data.include.filename = include_file; + confdata->data.include.modifiable = 0; + + if (push_file(include_file)) { + YY_NEW_FILE; + } +} + + +"#".+$ {/* Ignore comments */ + record_whitespace(yytext); +} + +[[:blank:]\n]+ { /* Eat whitespace */ + record_whitespace(yytext); + char *ypos = yytext - 1; + while ((ypos = strchr(ypos + 1, '\n'))) + ++conf_line_nr; +} + +<> { + yy_delete_buffer( YY_CURRENT_BUFFER ); + yyterminate(); +} + +.* { + char *text = sci_strdup(yytext); + conf_parse_t *parse = next_conf_parse(CONF_PARSE_TYPE_LEXERROR); + parse->data.whitespace = text; +} + +%% + +static const char * +skip_while(const char *start, int (*predicate)(int)) +{ + const char *pos = start; + + while (*pos && predicate(*pos)) + ++pos; + + return pos; +} + +static const char * +skip_until(const char *start, int (*predicate)(int)) +{ + const char *pos = start; + + while (*pos && (!predicate(*pos))) + ++pos; + + return pos; +} + +static char * +string_between(const char *start, const char *end) +{ + int length = end - start; + assert(length >= 0); + char * result = sci_malloc(length + 1); + memcpy(result, start, length); + result[length] = 0; /* terminate */ + + return result; +} + +static int +is_separator_or_alnum(int character) +{ + return isalnum(character) + || (character == '.') + || (character == '_') + || (character == '-'); +} + +static int +is_double_quote(int character) +{ + return character == '"'; +} + +static int +is_period(int character) +{ + return character == '.'; +} + +int +yywrap(void) +{ + return pop_file(); /* no further input */ +} + +static int +push_file(char *name) +{ + file_stack_t *newfs; + file_stack_t *s; + FILE *newfile; + + if (yy_filename && !strcmp(name, yy_filename)) { + fprintf(stderr, "[conf] Warning: Attempted immediate circular inclusion of config file '%s'\n", + name); + return 1; + } + + s = file_stack; + while (s) { + if (!strcmp(name, s->name)) { + fprintf(stderr, "[conf] Warning: Attempted circular inclusion of config file '%s'\n", + name); + return 1; + } + s = s->next; + } + + if (!(newfile = fopen(name, "r"))) { + fprintf(stderr, "[conf] Warning: Could not open configuration file '%s'\n", name); + return 1; + } + + if (yyin) { + newfs = malloc(sizeof(struct _file_stack)); + newfs->handle = yy_fsci_active_buffer; + newfs->name = yy_filename; + + newfs->next = file_stack; + newfs->line_nr = conf_line_nr; + newfs->input_spec = current_confparse; + current_confparse = NULL; + assert(newfs->input_spec->type == CONF_PARSE_TYPE_INCLUDE); + current_confparse_start = &(newfs->input_spec->data.include.options_head); + + file_stack = newfs; + } + + yy_filename = sci_strdup(name); + yy_fsci_active_buffer = yy_create_buffer(newfile, YY_BUF_SIZE); + yy_switch_to_buffer(yy_fsci_active_buffer); + + return 0; +} + +static int +pop_file(void) +{ + if (file_stack) { + void *goner = file_stack; + yy_delete_buffer(yy_fsci_active_buffer); + fclose(yyin); + yy_fsci_active_buffer = file_stack->handle; + yy_switch_to_buffer(yy_fsci_active_buffer); + + free(yy_filename); + yy_filename = file_stack->name; + conf_line_nr = file_stack->line_nr; + + /* Recover confparse */ + assert(file_stack->input_spec->type == CONF_PARSE_TYPE_INCLUDE); + file_stack->input_spec->data.include.options_end = current_confparse; + current_confparse = file_stack->input_spec; + current_confparse_start = NULL; + + file_stack = file_stack->next; + + free(goner); + return 0; + } else { + if (yy_filename) { + free(yy_filename); + yy_filename = NULL; + } + if (yyin) { + yy_delete_buffer(yy_fsci_active_buffer); + fclose(yyin); + yyin = NULL; + } + return 1; /* Done */ + } +} + + +static void +record_whitespace(const char *yytext) +{ + char *text = sci_strdup(yytext); + conf_parse_t *parse = next_conf_parse(CONF_PARSE_TYPE_WHITESPACE); + parse->data.whitespace = text; +} + + +static conf_parse_t * +next_conf_parse (int type) +{ + conf_parse_t *result; + + fprintf(stderr, "Allocing entry...\n"); + result = sci_malloc(sizeof(conf_parse_t)); + fprintf(stderr, "Done...\n"); + + if (!type) { + BREAKPOINT(); + } + + result->type = type; + result->line_nr = conf_line_nr; + result->next = NULL; + result->overridden = NULL; + + if (current_confparse) { + current_confparse->next = result; + result->prev = current_confparse; + } else { + result->prev = NULL; + } + + if (current_confparse_start) { + *current_confparse_start = result; + current_confparse_start = NULL; + } + + current_confparse = result; + + fprintf(stderr, "Final...\n"); + return result; +} + +/* -------------- */ +/* API operations */ +/* -------------- */ + +conf_parse_t * +conf_read_file(char *config_file_name, int modifiable, conf_parse_t *previous) +{ + char *conf_path = sci_strdup(config_file_name); + + conf_parse_t *confdata = next_conf_parse(CONF_PARSE_TYPE_INCLUDE); + confdata->data.include.include_prefix = NULL; + confdata->data.include.include_suffix = NULL; + confdata->data.include.filename = conf_path; + confdata->data.include.modifiable = modifiable; + + if (previous) { + /* Make sure to hook confdata in after previous */ + conf_parse_t **termp = &previous; + + while ((*termp)->next) + termp = &((*termp)->next); + + (*termp)->next = confdata; /* Hook into the final pointer */ + } + + if ((push_file(conf_path))) { + sci_free(conf_path); + return NULL; + } + + + current_confparse_start = &confdata->data.include.options_head; + yylex(); /* Parse the file */ + confdata->data.include.options_end = current_confparse; + confdata->next = NULL; + + while (!pop_file()); /* Ignore error conditions- might be lex implementation dependant */ + + if (previous) + return previous; + else + return confdata; +} + +#define XFREE(x) if (x) sci_free(x); + +void +conf_free_parse(conf_parse_t *raw_config) +{ + while (raw_config) { + switch (raw_config->type) { + + case CONF_PARSE_TYPE_WHITESPACE: + sci_free(raw_config->data.whitespace); + break; + + case CONF_PARSE_TYPE_SUBSECTION: + sci_free(raw_config->data.subsection.pre_whitespace); + sci_free(raw_config->data.subsection.name); + sci_free(raw_config->data.subsection.post_whitespace); + break; + + case CONF_PARSE_TYPE_OPTION: + XFREE(raw_config->data.assignment.subsystem); + XFREE(raw_config->data.assignment.driver); + sci_free(raw_config->data.assignment.option); + sci_free(raw_config->data.assignment.centre_whitespace); + sci_free(raw_config->data.assignment.value); + sci_free(raw_config->data.assignment.terminal_whitespace); + break; + + case CONF_PARSE_TYPE_INCLUDE: + XFREE(raw_config->data.include.include_prefix); + XFREE(raw_config->data.include.include_suffix); + sci_free(raw_config->data.include.filename); + conf_free_parse(raw_config->data.include.options_head); /* recurse */ + break; + + case CONF_PARSE_TYPE_EXTENSION: + conf_extension_free(raw_config->data.extension); + raw_config->data.extension = NULL; + break; + + case CONF_PARSE_TYPE_LEXERROR: + sci_free(raw_config->data.whitespace); + break; + + default: + fprintf(stderr, "[conf] INTERNAL ERROR: Trying to free config segment of invalid type %d\n", raw_config->type); + BREAKPOINT(); + } + + raw_config->type = -1; + conf_parse_t *next = raw_config->next; + sci_free(raw_config); + raw_config = next; + } +} + +#undef DEBUG_CONF_PRINTING + +#ifdef DEBUG_CONF_PRINTING +#define fputs(data, file) my_fputs(data, file) +#define fopen(filename, mode) my_fopen(filename, mode) +#define fclose(file) my_fclose(file) + +FILE * +my_fopen(const char *filename, const char *mode) +{ + printf(">>> Opening ``%s'' for [%s]\n", filename, mode); + return (FILE *) filename; +} + +int +my_fputs(const char *data, const FILE *file) +{ + return puts(data); +} + +int +fclose(const FILE *file) +{ + printf(">>> Closing ``%s''\n", file); + return 0; +} +#endif + +static void +conf_write_parse_file(conf_parse_t *confparse, FILE *file) +{ + while (confparse) { + switch (confparse->type) { + + case CONF_PARSE_TYPE_LEXERROR: + case CONF_PARSE_TYPE_WHITESPACE: + fputs(confparse->data.whitespace, file); + break; + + case CONF_PARSE_TYPE_SUBSECTION: + fputs(confparse->data.subsection.pre_whitespace, file); + fputs(confparse->data.subsection.name, file); + fputs(confparse->data.subsection.post_whitespace, file); + break; + + case CONF_PARSE_TYPE_OPTION: + if (confparse->data.assignment.subsystem) { + fputs(confparse->data.assignment.subsystem, file); + fputs(".", file); + } + if (confparse->data.assignment.driver) { + fputs(confparse->data.assignment.driver, file); + fputs(".", file); + } + fputs(confparse->data.assignment.option, file); + fputs(confparse->data.assignment.centre_whitespace, file); + fputs(confparse->data.assignment.value, file); + fputs(confparse->data.assignment.terminal_whitespace, file); + break; + + case CONF_PARSE_TYPE_INCLUDE: + if (confparse->data.include.include_prefix) { + fputs(confparse->data.include.include_prefix, file); + fputs(confparse->data.include.filename, file); + fputs(confparse->data.include.include_suffix, file); + } + conf_write_parse(confparse); /* Recurse indirectly */ + break; + + case CONF_PARSE_TYPE_EXTENSION: + conf_extension_print(file, confparse->data.extension); + break; + + default: + fprintf(stderr, "[conf] INTERNAL ERROR: Trying to print config segment of invalid type %d\n", confparse->type); + BREAKPOINT(); + } + + confparse = confparse->next; + } +} + +void +conf_write_parse(conf_parse_t *raw_config) +{ + while (raw_config) { + switch (raw_config->type) { + + case CONF_PARSE_TYPE_WHITESPACE: + case CONF_PARSE_TYPE_SUBSECTION: + case CONF_PARSE_TYPE_OPTION: + case CONF_PARSE_TYPE_EXTENSION: + case CONF_PARSE_TYPE_LEXERROR: + break; + + case CONF_PARSE_TYPE_INCLUDE: + if (raw_config->data.include.modifiable) { + const char *filename = raw_config->data.include.filename; + FILE *file = fopen(filename, "w"); + if (!file) { + fprintf(stderr, "[conf] Could not write back to file ``%s'': ", filename); + perror(NULL); + } else { + conf_write_parse_file(raw_config->data.include.options_head, file); + fclose(file); + } + } +#ifdef DEBUG_CONF_PRINTING + else printf(">>> Skipping file ``%s'' for output since it's tagged read-only\n", raw_config->data.include.filename); +#endif + break; + + default: + fprintf(stderr, "[conf] INTERNAL ERROR: Trying to print config segment of invalid type %d (on top-level)\n", raw_config->type); + BREAKPOINT(); + } + + raw_config = raw_config->next; + } +} + +conf_extension_t * +conf_extension_alloc(int type) +{ + conf_extension_t *retval = sci_malloc(sizeof(conf_extension_t)); + retval->type = type; + return retval; +} diff --git a/engines/sci/config/config.test b/engines/sci/config/config.test new file mode 100644 index 0000000000..cb843a84c5 --- /dev/null +++ b/engines/sci/config/config.test @@ -0,0 +1,48 @@ +pic0_dither_mode = dither256 +pic0_brush_mode = random_ellipses +pic0_scaled = yes +view_filter = trilinear +pic_filter = linear +cursor_filter = bilinear +text_filter = linear +#pic_antialiasing = simple +midiout.alsaraw.card = 0 +debugflags = +a + +[qg1] + +resource_dir = /home/creichen/work/port/sci/qg1 + +[sq1] + +resource_dir = /home/creichen/work/port/sci/sq1 + +[sq4] + +resource_dir = /home/creichen/work/port/sci/sq4 + +[qg2] + +pic(*) *= (0.7, 0.7, 0.7); +resource_dir = /home/creichen/work/port/sci/qg2 +version = 1.000.120 + +[cb1] + +resource_dir = /home/creichen/work/port/sci/rei/cb1 + +[lb1] + +resource_dir = /home/creichen/work/port/sci/amiga/cb1 + + +[lsl1] + +resource_dir = /home/creichen/work/port/sci/lsl1 +version = 1.000.577 +pic0_scaled = false + +[lsl2] + +resource_dir = /home/creichen/work/port/sci/lsl2 +%include \ No newline at end of file diff --git a/engines/sci/config/dynlink.c b/engines/sci/config/dynlink.c new file mode 100644 index 0000000000..a1877dcd64 --- /dev/null +++ b/engines/sci/config/dynlink.c @@ -0,0 +1,69 @@ +/*************************************************************************** + Copyright (C) 2008 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantability, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#ifdef HAVE_DYNAMIC_LINKING +#include + +void * +dynlink_open(const char *filename) +{ + void *retval = dlopen(filename, RTLD_NOW /* Quick error handling */ +#ifdef RTLD_DEEPBIND + | RTLD_DEEPBIND /* Enforce deep binding, if possible */ +#endif + ); + if (retval == NULL) + fprintf(stderr, "[dynlink] %s: %s\n", filename, dlerror()); + + return retval; +} + +void * +dynlink_resolve(void *handle, const char *symname) +{ + return dlsym(handle, symname); +} + +void +dynlink_close(void *handle) +{ + if (dlclose(handle)) + fprintf(stderr, "[dynlink] When unloading module: %s\n", filename, dlerror()); +} + +char *default_paths[] = { + NULL +}; + +char ** +dynlink_paths(void) +{ + return default_paths; +} + +#endif /* !defined(HAVE_DLFCN_H_) */ diff --git a/engines/sci/config/extension.c b/engines/sci/config/extension.c new file mode 100644 index 0000000000..531b083f5a --- /dev/null +++ b/engines/sci/config/extension.c @@ -0,0 +1,66 @@ +/*************************************************************************** + Copyright (C) 2008 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantability, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +#include "conf_extension.h" +#include "sci_memory.h" + +int +conf_extension_supercedes(conf_extension_t *a, conf_extension_t *b) +{ + return 0; +} + +void +conf_extension_print(FILE *file, conf_extension_t *a) +{ + switch (a->type) { + + case CONF_EXT_TYPE_GFX: + fputs(a->data, file); + break; + + default: + fprintf(stderr, "[conf] Unknown config extension type %d", a->type); + } +} + +void +conf_extension_free(conf_extension_t *a) +{ + switch (a->type) { + + case CONF_EXT_TYPE_GFX: + sci_free(a->data); + break; + + default: + fprintf(stderr, "[conf] Unknown config extension type %d", a->type); + } + + a->type = CONF_EXT_TYPE_INVALID; + a->data = NULL; + sci_free(a); +} diff --git a/engines/sci/config/lsl2.scifx b/engines/sci/config/lsl2.scifx new file mode 100644 index 0000000000..7dccd2fb56 --- /dev/null +++ b/engines/sci/config/lsl2.scifx @@ -0,0 +1,43 @@ +# LSL2 per-resource palette configuration +# Copyright (C) 2006 Matt Hargett + +#ego +view(507,155,161,163,170,197,198,431,432,145..159,131..141,191,104,244,215,217,219,100,101,110,111,112,113,192,193,604,704..706,717,718,818,819,822,823,824,831,832,833,834,835) *= 1.25; +pic(28,99) *= 1.25; +view(218)(3) *= 1.25; +view(218)(0..2) *= 0.9; +view(820)(0,1) *= 0.9; +view(227,224,206,208,209,220) *= 0.9; +view(221,254,252) *= 0.7; +view(820)(2..4) *= 1.2; +view(816)(5)(0) *= 1.2; +view(516,509,501..504,401..403,408,409,411,413,414,417,418,419,430,310,302,303,120..124,232,223,208,710,716,714) *=1.1; +view(434..438,311,313,316,319,321,323,324,306..309,248,245,246,233..235,237,226,229,222,203,204,205,600,525,524,523,522,520,602,605,608,707..708) *=1.2; +view(305)(4) *= 1.1; +view(305)(0..3) *= 0.6; +view(661)(0,1,3..5) *= 1.2; +view(661)(2) *= 0.7; +view(711,712,713,60) *= (0.9, 1.0, 1.0); +view(816)(0..4) *= 0.9; +view(506,508,500,252,803,804,433) *= 0.6; +view(513)(0..5) *= 0.5; +view(240..243,701,722) *= 0.8; +view(700)(1) *= (0.6, 0.9, 1.0); +view(610,611) *= (0.9, 1.0, 1.1); +view(607)(1) *= 0.8; +view(253,228,247,300,326) *= 0.8; +view(412) *= 1.3; +pic(96) *= 1.1; + +pic(92,93,18,20..26,134,40,50,76..82,181) *= 0.9; +pic(114..118,125,11..17,19,43,70..74,44,86,101..104,32,33,35,36,95) *= 0.85; +#titles +view(800,801) *= 1.5; +pic(10,90,91) *= 0.4; + +#misc effects +view(702) *= (1.1, 1.0, 1.0) +view(519) *= 0.8; + +view(200)(0) *= 0.7; +view(201,202) *= 0.8; diff --git a/engines/sci/config/option-test.conf b/engines/sci/config/option-test.conf new file mode 100644 index 0000000000..c556a763cc --- /dev/null +++ b/engines/sci/config/option-test.conf @@ -0,0 +1 @@ +# Fill me in! diff --git a/engines/sci/config/sq3.scifx b/engines/sci/config/sq3.scifx new file mode 100644 index 0000000000..8e1e9afd28 --- /dev/null +++ b/engines/sci/config/sq3.scifx @@ -0,0 +1,81 @@ +# SQ3 per-resource palette configuration +# Copyright (C) 2006 Matt Hargett + +# ego +view(0,8,11,12,14,68,17,22..26,32,35, 751, 289, 288, 261, 260, 257, 213, 199, 193, 192, 138, 137, 134, 109, 110, 113, 114, 117, 122, 123,100, 99, 97, 95, 89, 88, 87, 85, 84, 82, 76, 68, 63, 104 ) *= 1.25 ; +view(136) *= 1.15; +view(106)(4,5,9) *= 1.25; +view(105)(0,1) *= 1.25; +# ego on garbage lifter -- lighten but not so as to make the lifter be obviously weird +view(13)(0)(2) *= 1.15 ; +view(31) *= 1.15; +view(15)(3)(0) *= 1.25 ; +view(16,19)(0) *= 1.25; +view(57)(5..6) *= 1.25 ; +view(21)(1) *= 1.25 ; +# ego's shadow +view(7,18) *= 0.5; +view(6..8,18) *= 0.9; +view(901) *= 1.25; +view(751) *= 1.25; +view(750)(1) *= 1.25; +view(92)(4) *= 1.25; +view(83)(0) *= 1.25; +view(83)(1)(2) *=1.15; +view(83)(2)(2) *=1.15; +view(78)(3) *= 1.25; +view(64)(2,3) *= 1.25; +# ego's hands controlling robot +pic(96) *= 1.15; +# peeking at scumsoft +pic(81,82) *= 1.15; +#lifted by robot +pic(430) *= 1.15; + +# computer controls +view(40,41,42,149,146,141,151,152) *= 0.8; +view(70,2) *= 0.9; +pic(17,18,162..164,170,180,191,300) *= 0.75; + +# title screen +view(900) *= 0.9; +pic(1) *= 0.9 ; +pic(926) *= 0.9; + +# humans(?) +view(593,93,103,131..133,210,130,115) *= 1.2; +pic(117) *=1.15; +view(116)(1..2) *= 1.2; + +#ships, planets, and robots +view(39) *= 0.9; +view(1) *= 0.75; +pic(205..209,112..115) *= 0.9; +pic(60..72) *= 0.9; +pic(153) *= 0.8; +view(96) *= 0.9; +pic(690) *= 0.9; +view(77)(0..2) *= 0.7; +view(259) *= 1.15; + + +# in the garbage scow +pic(1..20) *= 0.75 ; +pic(157) *= 0.6; +view(20)(0) *= 0.5; +# rats +view(15)(0,1) *= 0.6; +view(34) *= 0.6; + +# guys from andromeda +view(128) *= 0.9; +view(601, 602) *= 0.9; + +# misc text bubbles, effects, etc +view(94) *= 1.1; +view(91, 73) *= 1.5; +view(57)(3,4) *= 1.5; +view(15)(4) *= 1.5; +view(64)(0) *= 1.5; +view(71)(8) *= 1.5; +view(10)(6) *= 1.5; diff --git a/engines/sci/config/test-options.c b/engines/sci/config/test-options.c new file mode 100644 index 0000000000..d4a44cc928 --- /dev/null +++ b/engines/sci/config/test-options.c @@ -0,0 +1,91 @@ +/*************************************************************************** + Copyright (C) 2008 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantability, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include "conf_parse.h" +#include "conf_subsystems.h" + + +int error_count = 0; + +#define ASSERT(x) if (!(x)) { ++error_count; fprintf(stderr, "[error] Assertion failed at line %d: " #x "\n", __LINE__);} + +char ** dynamic_driver_locations = { + NULL +}; + +char *fsci_driver_magic = CONF_DRIVER_MAGIC; + +static conf_main_t main_drv = { + .header = { + .magic = CONF_DRIVER_MAGIC; + .freesci_version = CONF_DRIVER_VERSION; + .subsystem_id = 0; + .subsystem_version = 0; + .name = "config-test"; + .version = "1.0"; + .dependencies = CONF_DRIVER_DEPENDENCY_GFX; /* Get SFX dependency transitively from GFX */ + .options = main_options; + .set_option = + }; + .subsystems = { &sub_gfx, &sub_sound, + &sub_ignore, &sub_ignore, &sub_ignore, + &sub_ignore, &sub_ignore }; + .dynamic_driver_locations = dynamic_driver_locations; + .init = init_main; + .exit = exit_main; +}; + +void +test_validation(conf_parse_t *parse) +{ + ASSERT (NULL == conf_validate (&main_drv, parse)); +} + +void +test_ + +int +main(int argc, char **argv) +{ + int i; + + conf_parse_t *conf = = conf_read_file("option-test.conf", 1, conf); + + printf (">>> Parsing complete.\n"); + ASSERT(conf != NULL); + if (!conf) + return 1; + conf_write_parse(conf); + + test_validation(parse); + test_setting(pase); + + printf (">>> Cleanup...\n"); + conf_free_parse(conf); + + return error_count != 0; +} diff --git a/engines/sci/config/test-parse.c b/engines/sci/config/test-parse.c new file mode 100644 index 0000000000..548bc8780b --- /dev/null +++ b/engines/sci/config/test-parse.c @@ -0,0 +1,59 @@ +/*************************************************************************** + Copyright (C) 2007 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include "conf_parse.h" + +int +main(int argc, char **argv) +{ + int i; + + if (argc < 2) { + printf("Usage: %s +\n", argv[0]); + return 1; + } + + conf_parse_t *conf = NULL; + + for (i = 1; i < argc; i++) { + conf_parse_t *newconf = conf_read_file(argv[i], 1, conf); + if (newconf == NULL) { + fprintf(stderr, "Couldn't parse ``%s''\n", argv[i]); + conf_free_parse(conf); + return 1; + } else + conf = newconf; + } + + printf (">>> Parsing complete. Re-emitting parsed files...\n"); + conf_write_parse(conf); + printf (">>> Cleanup...\n"); + conf_free_parse(conf); + + return 0; +} diff --git a/engines/sci/dc/3dutils.c b/engines/sci/dc/3dutils.c new file mode 100644 index 0000000000..b60334c841 --- /dev/null +++ b/engines/sci/dc/3dutils.c @@ -0,0 +1,217 @@ +/* + * Copyright 2000, 2001, 2002 + * Dan Potter. All rights reserved. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Cryptic Allusion nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Modified by Walter van Niftrik */ + +#include +#include +#include "gp.h" + +#include "sintab.h" + +/* Some misc 3D utils */ + +/* Rotate a 3-pair of coordinates by the given angle 0..255 */ +void rotate(int zang, int xang, int yang, float *x, float *y, float *z) { + float tx, ty, tz; + + tx = (mcos(zang)* *x - msin(zang)* *y); + ty = (mcos(zang)* *y + msin(zang)* *x); + *x = tx; *y = ty; + + tz = (mcos(xang)* *z - msin(xang)* *y); + ty = (mcos(xang)* *y + msin(xang)* *z); + *y = ty; *z = tz; + + tx = (mcos(yang)* *x - msin(yang)* *z); + tz = (mcos(yang)* *z + msin(yang)* *x); + *x = tx; *z = tz; +} + +/* Draw the mouse cursor at the given location */ +void draw_poly_mouse(int ptrx, int ptry, float alpha) { + pvr_vertex_t vert; + + /* Start a textured polygon set (with the font texture) */ + pvr_prim(&util_txr_hdr, sizeof(util_txr_hdr)); + + vert.flags = PVR_CMD_VERTEX; + vert.x = ptrx; + vert.y = ptry + 16.0f; + vert.z = 512.0f + alpha; + vert.u = 0.0f; + vert.v = 16.0f / 256.0f; + vert.argb = PVR_PACK_COLOR(0.80f * alpha, 1.0f, 1.0f, 1.0f); + vert.oargb = 0; + pvr_prim(&vert, sizeof(vert)); + + vert.x = ptrx; + vert.y = ptry; + vert.u = 0.0f; + vert.v = 0.0f; + pvr_prim(&vert, sizeof(vert)); + + vert.x = ptrx + 10.0f; + vert.y = ptry + 16.0f; + vert.u = 10.0f / 256.0f; + vert.v = 16.0f / 256.0f; + pvr_prim(&vert, sizeof(vert)); + + vert.flags = PVR_CMD_VERTEX_EOL; + vert.x = ptrx + 10.0f; + vert.y = ptry; + vert.u = 10.0f / 256.0f; + vert.v = 0.0f; + pvr_prim(&vert, sizeof(vert)); +} + +/* Draw one font character (12x24); assumes polygon header already sent */ +void draw_poly_char(float x1, float y1, float z1, float a, float r, float g, float b, int c) { + pvr_vertex_t vert; + int ix = (c % 16) * 16; + int iy = (c / 16) * 24; + float u1 = ix * 1.0f / 256.0f; + float v1 = iy * 1.0f / 256.0f; + float u2 = (ix+12) * 1.0f / 256.0f; + float v2 = (iy+24) * 1.0f / 256.0f; + + vert.flags = PVR_CMD_VERTEX; + vert.x = x1; + vert.y = y1 + 24.0f; + vert.z = z1; + vert.u = u1; + vert.v = v2; + vert.argb = PVR_PACK_COLOR(a,r,g,b); + vert.oargb = 0; + pvr_prim(&vert, sizeof(vert)); + + vert.x = x1; + vert.y = y1; + vert.u = u1; + vert.v = v1; + pvr_prim(&vert, sizeof(vert)); + + vert.x = x1 + 12.0f; + vert.y = y1 + 24.0f; + vert.u = u2; + vert.v = v2; + pvr_prim(&vert, sizeof(vert)); + + vert.flags = PVR_CMD_VERTEX_EOL; + vert.x = x1 + 12.0f; + vert.y = y1; + vert.u = u2; + vert.v = v1; + pvr_prim(&vert, sizeof(vert)); +} + +/* Draw a set of textured polygons at the given depth and color that + represent a string of text. */ +static char strbuf[1024]; +void draw_poly_strf(float x1, float y1, float z1, float a, float r, + float g, float b, char *fmt, ...) { + va_list args; + char *s; + + va_start(args, fmt); + vsprintf(strbuf, fmt, args); + va_end(args); + + pvr_prim(&util_txr_hdr, sizeof(util_txr_hdr)); + s = strbuf; + while (*s) { + if (*s == ' ') { + x1 += 12.0f; s++; + } else { + draw_poly_char(x1+=12.0f, y1, z1, a, r, g, b, *s++); + } + } +} + +/* Draw a horizontally centered set of textured polygons at the given depth + and color that represent a string of text. Only for video mode 640x480. */ +void draw_poly_strf_ctr(float y1, float z1, float a, float r, float g, float b, + char *fmt, ...) { + float x1; + va_list args; + char *s; + + va_start(args, fmt); + vsnprintf(strbuf, 1024, fmt, args); + va_end(args); + + x1 = 320.0f - strlen(strbuf) * 6.0f; + + pvr_prim(&util_txr_hdr, sizeof(util_txr_hdr)); + s = strbuf; + while (*s) { + if (*s == ' ') { + x1 += 12.0f; s++; + } else { + draw_poly_char(x1+=12.0f, y1, z1, a, r, g, b, *s++); + } + } +} + +/* Draw a polygon for a shaded box; wow, a nasty looking func =) */ +void draw_poly_box(float x1, float y1, float x2, float y2, float z, + float a1, float r1, float g1, float b1, + float a2, float r2, float g2, float b2) { + pvr_poly_cxt_t cxt; + pvr_poly_hdr_t poly; + pvr_vertex_t vert; + + pvr_poly_cxt_col(&cxt, PVR_LIST_TR_POLY); + pvr_poly_compile(&poly, &cxt); + pvr_prim(&poly, sizeof(poly)); + + vert.flags = PVR_CMD_VERTEX; + vert.x = x1; vert.y = y2; vert.z = z; + vert.argb = PVR_PACK_COLOR( + (a1+a2)/2, + (r1+r2)/2, + (g1+g2)/2, + (b1+b2)/2); + vert.oargb = 0; + pvr_prim(&vert, sizeof(vert)); + + vert.y = y1; + vert.argb = PVR_PACK_COLOR(a1, r1, g1, b1); + pvr_prim(&vert, sizeof(vert)); + + vert.x = x2; vert.y = y2; + vert.argb = PVR_PACK_COLOR(a2, r2, g2, b2); + pvr_prim(&vert, sizeof(vert)); + + vert.flags = PVR_CMD_VERTEX_EOL; + vert.y = y1; + vert.argb = PVR_PACK_COLOR( + (a1+a2)/2, + (r1+r2)/2, + (g1+g2)/2, + (b1+b2)/2); + pvr_prim(&vert, sizeof(vert)); +} diff --git a/engines/sci/dc/Makefile b/engines/sci/dc/Makefile new file mode 100644 index 0000000000..f8e5227e34 --- /dev/null +++ b/engines/sci/dc/Makefile @@ -0,0 +1,168 @@ +BIN_TARGET = freesci.bin +ELF_TARGET = freesci.elf + +FSCI_BASE = ../.. + +OBJS = \ + $(FSCI_BASE)/src/config.o \ + 3dutils.o \ + aica_drv.o \ + bkg.o \ + dc_save.o \ + gamemenu.o \ + keyboard.o \ + selectgame.o \ + snd_stream.o \ + texture.o \ + $(FSCI_BASE)/src/engine/game.o \ + $(FSCI_BASE)/src/engine/gc.o \ + $(FSCI_BASE)/src/engine/grammar.o \ + $(FSCI_BASE)/src/engine/kernel.o \ + $(FSCI_BASE)/src/engine/kevent.o \ + $(FSCI_BASE)/src/engine/kfile.o \ + $(FSCI_BASE)/src/engine/kgraphics.o \ + $(FSCI_BASE)/src/engine/klists.o \ + $(FSCI_BASE)/src/engine/kmath.o \ + $(FSCI_BASE)/src/engine/kmenu.o \ + $(FSCI_BASE)/src/engine/kmovement.o \ + $(FSCI_BASE)/src/engine/kpathing.o \ + $(FSCI_BASE)/src/engine/kscripts.o \ + $(FSCI_BASE)/src/engine/ksound.o \ + $(FSCI_BASE)/src/engine/kstring.o \ + $(FSCI_BASE)/src/engine/said.o \ + $(FSCI_BASE)/src/engine/savegame.o \ + $(FSCI_BASE)/src/engine/scriptconsole.o \ + $(FSCI_BASE)/src/engine/scriptdebug.o \ + $(FSCI_BASE)/src/engine/seg_manager.o \ + $(FSCI_BASE)/src/engine/sys_strings.o \ + $(FSCI_BASE)/src/engine/vm.o \ + $(FSCI_BASE)/src/gfx/antialias.o \ + $(FSCI_BASE)/src/gfx/drivers/dc_driver.o \ + $(FSCI_BASE)/src/gfx/drivers/gfx_drivers.o \ + $(FSCI_BASE)/src/gfx/drivers/null_driver.o \ + $(FSCI_BASE)/src/gfx/font.o \ + $(FSCI_BASE)/src/gfx/font-5x8.o\ + $(FSCI_BASE)/src/gfx/font-6x10.o\ + $(FSCI_BASE)/src/gfx/gfx_console.o \ + $(FSCI_BASE)/src/gfx/gfx_resource.o \ + $(FSCI_BASE)/src/gfx/gfx_res_options.o \ + $(FSCI_BASE)/src/gfx/gfx_support.o \ + $(FSCI_BASE)/src/gfx/gfx_tools.o \ + $(FSCI_BASE)/src/gfx/menubar.o \ + $(FSCI_BASE)/src/gfx/operations.o \ + $(FSCI_BASE)/src/gfx/resmgr.o \ + $(FSCI_BASE)/src/gfx/resource/sci_cursor_0.o \ + $(FSCI_BASE)/src/gfx/resource/sci_font.o \ + $(FSCI_BASE)/src/gfx/resource/sci_pal_1.o \ + $(FSCI_BASE)/src/gfx/resource/sci_pic_0.o \ + $(FSCI_BASE)/src/gfx/resource/sci_resmgr.o \ + $(FSCI_BASE)/src/gfx/resource/sci_view_0.o \ + $(FSCI_BASE)/src/gfx/resource/sci_view_1.o \ + $(FSCI_BASE)/src/gfx/sbtree.o \ + $(FSCI_BASE)/src/gfx/sci_widgets.o \ + $(FSCI_BASE)/src/gfx/widgets.o \ + $(FSCI_BASE)/src/main.o \ + $(FSCI_BASE)/src/menu/game_select_init.o \ + $(FSCI_BASE)/src/menu/game_select_screen.o \ + $(FSCI_BASE)/src/scicore/aatree.o \ + $(FSCI_BASE)/src/scicore/console.o \ + $(FSCI_BASE)/src/scicore/decompress0.o \ + $(FSCI_BASE)/src/scicore/decompress01.o \ + $(FSCI_BASE)/src/scicore/decompress1.o \ + $(FSCI_BASE)/src/scicore/decompress11.o \ + $(FSCI_BASE)/src/scicore/exe.o \ + $(FSCI_BASE)/src/scicore/exe_lzexe.o \ + $(FSCI_BASE)/src/scicore/exe_raw.o \ + $(FSCI_BASE)/src/scicore/fnmatch.o \ + $(FSCI_BASE)/src/scicore/int_hashmap.o \ + $(FSCI_BASE)/src/scicore/modules.o \ + $(FSCI_BASE)/src/scicore/old_objects.o \ + $(FSCI_BASE)/src/scicore/reg_t_hashmap.o \ + $(FSCI_BASE)/src/scicore/resource.o \ + $(FSCI_BASE)/src/scicore/resource_map.o \ + $(FSCI_BASE)/src/scicore/resource_patch.o \ + $(FSCI_BASE)/src/scicore/sci_memory.o \ + $(FSCI_BASE)/src/scicore/script.o \ + $(FSCI_BASE)/src/scicore/tools.o \ + $(FSCI_BASE)/src/scicore/versions.o \ + $(FSCI_BASE)/src/scicore/vocab.o \ + $(FSCI_BASE)/src/scicore/vocab_debug.o \ + $(FSCI_BASE)/src/sfx/iterator.o \ + $(FSCI_BASE)/src/sfx/songlib.o \ + $(FSCI_BASE)/src/sfx/core.o \ + $(FSCI_BASE)/src/sfx/adlib.o \ + $(FSCI_BASE)/src/sfx/pcm-iterator.o \ + $(FSCI_BASE)/src/sfx/time.o \ + $(FSCI_BASE)/src/sfx/player/players.o \ + $(FSCI_BASE)/src/sfx/player/realtime.o \ + $(FSCI_BASE)/src/sfx/player/polled.o \ + $(FSCI_BASE)/src/sfx/device/devices.o \ + $(FSCI_BASE)/src/sfx/timer/timers.o \ + $(FSCI_BASE)/src/sfx/timer/pthread.o \ + $(FSCI_BASE)/src/sfx/seq/sequencers.o \ + $(FSCI_BASE)/src/sfx/mixer/mixers.o \ + $(FSCI_BASE)/src/sfx/mixer/soft.o \ + $(FSCI_BASE)/src/sfx/mixer/dc.o \ + $(FSCI_BASE)/src/sfx/pcm_device/pcm_devices.o \ + $(FSCI_BASE)/src/sfx/pcm_device/audiobuf.o \ + $(FSCI_BASE)/src/sfx/seq/mt32.o \ + $(FSCI_BASE)/src/sfx/seq/gm.o \ + $(FSCI_BASE)/src/sfx/seq/instrument-map.o \ + $(FSCI_BASE)/src/sfx/softseq/softsequencers.o \ + $(FSCI_BASE)/src/sfx/softseq/SN76496.o \ + $(FSCI_BASE)/src/sfx/softseq/amiga.o \ + $(FSCI_BASE)/src/sfx/softseq/fmopl.o \ + $(FSCI_BASE)/src/sfx/softseq/opl2.o \ + $(FSCI_BASE)/src/sfx/softseq/pcspeaker.o + +AICA_OBJS = \ + $(FSCI_BASE)/src/dc/aica_crt0.o \ + $(FSCI_BASE)/src/dc/aica_main.o \ + $(FSCI_BASE)/src/dc/aica_sup.o + +all: $(BIN_TARGET) + +aica_%.o: aica_%.c + $(DC_ARM_CC) $(DC_ARM_CFLAGS) $(DC_ARM_INCS) -c $< -o $@ + +aica_%.o: aica_%.s + $(DC_ARM_AS) $(DC_ARM_AFLAGS) $< -o $@ + +%.o: %.c config.h + $(KOS_CC) $(KOS_CFLAGS) $(KOS_LOCAL_CFLAGS) -D_arch_$(KOS_ARCH) $(KOS_ALL_INCS) -c $< -o $@ + +aica.drv: aica.elf + $(DC_ARM_OBJCOPY) -O binary $< $@ + +aica.elf: $(AICA_OBJS) + $(DC_ARM_CC) -Wl,-Ttext,0x00000000 -nostartfiles -nostdlib -e reset -o $@ $(AICA_OBJS) -lgcc + +aica_drv.o: aica.drv + $(KOS_BASE)/utils/bin2o/bin2o $< snd_stream_drv $@ + +KOS_LOCAL_CFLAGS := -I$(FSCI_BASE)/src/include -I$(FSCI_BASE)/src/dc \ + -I$(FSCI_BASE)/src/include/beos -D_DREAMCAST -DHAVE_CONFIG_H \ + -DWANT_CONSOLE -DYY_NEVER_INTERACTIVE + +clean: rm-elf rm-bin + -rm -f $(OBJS) config.h + +rm-elf: + -rm -f $(ELF_TARGET) + +rm-bin: + -rm -f $(BIN_TARGET) + +config.h: config.h.in ../../configure.ac + cp config.h.in config.h + sed -n -e 's/AM_INIT_AUTOMAKE(.*,[[:blank:]]*\([[:graph:]]*\)[[:blank:]]*)/#define VERSION "\1"/p' ../../configure.ac >> config.h + +$(ELF_TARGET): $(OBJS) + $(KOS_CC) $(KOS_CFLAGS) $(KOS_LDFLAGS) -o $(ELF_TARGET) $(KOS_START) \ + $(OBJS) $(OBJEXTRA) -lm -lz $(KOS_LIBS) + +$(BIN_TARGET): $(ELF_TARGET) + $(KOS_OBJCOPY) -R .stack -O binary $(ELF_TARGET) $(BIN_TARGET) + +run: $(BIN_TARGET) + $(KOS_LOADER) $(BIN_TARGET) diff --git a/engines/sci/dc/aica.h b/engines/sci/dc/aica.h new file mode 100644 index 0000000000..84ac2baed0 --- /dev/null +++ b/engines/sci/dc/aica.h @@ -0,0 +1,52 @@ +/* + * Copyright 2000, 2001, 2002 + * Dan Potter. All rights reserved. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Cryptic Allusion nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __AICA_H +#define __AICA_H + +/* volatile unsigned char *dc_snd_base = (unsigned char *)0x00800000; */ +#define dc_snd_base ((volatile unsigned char *)0x00800000) + +/* Some convienence macros */ +#define SNDREG32A(x) ((volatile unsigned long *)(dc_snd_base + (x))) +#define SNDREG32(x) (*SNDREG32A(x)) +#define SNDREG8A(x) (dc_snd_base + (x)) +#define SNDREG8(x) (*SNDREG8A(x)) +#define CHNREG32A(chn, x) SNDREG32A(0x80*(chn) + (x)) +#define CHNREG32(chn, x) (*CHNREG32A(chn, x)) +#define CHNREG8A(chn, x) SNDREG8A(0x80*(chn) + (x)) +#define CHNREG8(chn, x) (*CHNREG8A(chn, x)) + +void aica_init(); +void aica_play(int ch, int delay); +void aica_sync_play(uint32 chmap); +void aica_stop(int ch); +void aica_vol(int ch); +void aica_pan(int ch); +void aica_freq(int ch); +int aica_get_pos(int ch); + +#endif /* __AICA_H */ diff --git a/engines/sci/dc/aica_cmd_iface.h b/engines/sci/dc/aica_cmd_iface.h new file mode 100644 index 0000000000..115ece64b0 --- /dev/null +++ b/engines/sci/dc/aica_cmd_iface.h @@ -0,0 +1,155 @@ +/* + * Copyright 2000, 2001, 2002 + * Dan Potter. All rights reserved. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Cryptic Allusion nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __ARM_AICA_CMD_IFACE_H +#define __ARM_AICA_CMD_IFACE_H + +/* $Id: aica_cmd_iface.h 1486 2005-11-10 13:03:57Z waltervn $ */ + +#ifndef __ARCH_TYPES_H +typedef unsigned long uint8; +typedef unsigned long uint32; +#endif + +/* Command queue; one of these for passing data from the SH-4 to the + AICA, and another for the other direction. If a command is written + to the queue and it is longer than the amount of space between the + head point and the queue size, the command will wrap around to + the beginning (i.e., queue commands _can_ be split up). */ +typedef struct aica_queue { + uint32 head; /* Insertion point offset (in bytes) */ + uint32 tail; /* Removal point offset (in bytes) */ + uint32 size; /* Queue size (in bytes) */ + uint32 valid; /* 1 if the queue structs are valid */ + uint32 process_ok; /* 1 if it's ok to process the data */ + uint32 data; /* Pointer to queue data buffer */ +} aica_queue_t; + +/* Command queue struct for commanding the AICA from the SH-4 */ +typedef struct aica_cmd { + uint32 size; /* Command data size in dwords */ + uint32 cmd; /* Command ID */ + uint32 timestamp; /* When to execute the command (0 == now) */ + uint32 cmd_id; /* Command ID, for cmd/response pairs, or channel id */ + uint32 misc[4]; /* Misc Parameters / Padding */ + uint8 cmd_data[0]; /* Command data */ +} aica_cmd_t; + +/* Maximum command size -- 256 dwords */ +#define AICA_CMD_MAX_SIZE 256 + +/* This is the cmd_data for AICA_CMD_CHAN. Make this 16 dwords long + for two aica bus queues. */ +typedef struct aica_channel { + uint32 cmd; /* Command ID */ + uint32 base; /* Sample base in RAM */ + uint32 type; /* (8/16bit/ADPCM) */ + uint32 length; /* Sample length */ + uint32 loop; /* Sample looping */ + uint32 loopstart; /* Sample loop start */ + uint32 loopend; /* Sample loop end */ + uint32 freq; /* Frequency */ + uint32 vol; /* Volume 0-255 */ + uint32 pan; /* Pan 0-255 */ + uint32 pos; /* Sample playback pos */ + uint32 pad[5]; /* Padding */ +} aica_channel_t; + +/* Declare an aica_cmd_t big enough to hold an aica_channel_t + using temp name T, aica_cmd_t name CMDR, and aica_channel_t name CHANR */ +#define AICA_CMDSTR_CHANNEL(T, CMDR, CHANR) \ + uint8 T[sizeof(aica_cmd_t) + sizeof(aica_channel_t)]; \ + aica_cmd_t * CMDR = (aica_cmd_t *)T; \ + aica_channel_t * CHANR = (aica_channel_t *)(CMDR->cmd_data); +#define AICA_CMDSTR_CHANNEL_SIZE ((sizeof(aica_cmd_t) + sizeof(aica_channel_t))/4) + +/* Command values (for aica_cmd_t) */ +#define AICA_CMD_NONE 0x00000000 /* No command (dummy packet) */ +#define AICA_CMD_PING 0x00000001 /* Check for signs of life */ +#define AICA_CMD_CHAN 0x00000002 /* Perform a wavetable action */ +#define AICA_CMD_SYNC_CLOCK 0x00000003 /* Reset the millisecond clock */ + +/* Response values (for aica_cmd_t) */ +#define AICA_RESP_NONE 0x00000000 +#define AICA_RESP_PONG 0x00000001 /* Response to CMD_PING */ +#define AICA_RESP_DBGPRINT 0x00000002 /* Entire payload is a null-terminated string */ + +/* Command values (for aica_channel_t commands) */ +#define AICA_CH_CMD_MASK 0x0000000f + +#define AICA_CH_CMD_NONE 0x00000000 +#define AICA_CH_CMD_START 0x00000001 +#define AICA_CH_CMD_STOP 0x00000002 +#define AICA_CH_CMD_UPDATE 0x00000003 + +/* Start values */ +#define AICA_CH_START_MASK 0x00300000 + +#define AICA_CH_START_DELAY 0x00100000 /* Set params, but delay key-on */ +#define AICA_CH_START_SYNC 0x00200000 /* Set key-on for all selected channels */ + +/* Update values */ +#define AICA_CH_UPDATE_MASK 0x000ff000 + +#define AICA_CH_UPDATE_SET_FREQ 0x00001000 /* frequency */ +#define AICA_CH_UPDATE_SET_VOL 0x00002000 /* volume */ +#define AICA_CH_UPDATE_SET_PAN 0x00004000 /* panning */ + +/* Sample types */ +#define AICA_SM_8BIT 1 +#define AICA_SM_16BIT 0 +#define AICA_SM_ADPCM 2 + + +/* This is where our SH-4/AICA comm variables go... */ + +/* 0x000000 - 0x010000 are reserved for the program */ + +/* Location of the SH-4 to AICA queue; commands from here will be + periodically processed by the AICA and then removed from the queue. */ +#define AICA_MEM_CMD_QUEUE 0x010000 /* 32K */ + +/* Location of the AICA to SH-4 queue; commands from here will be + periodically processed by the SH-4 and then removed from the queue. */ +#define AICA_MEM_RESP_QUEUE 0x018000 /* 32K */ + +/* This is the channel base, which holds status structs for all the + channels. This is READ-ONLY from the SH-4 side. */ +#define AICA_MEM_CHANNELS 0x020000 /* 64 * 16*4 = 4K */ + +/* The clock value (in milliseconds) */ +#define AICA_MEM_CLOCK 0x021000 /* 4 bytes */ + +/* 0x021004 - 0x030000 are reserved for future expansion */ + +/* Open ram for sample data */ +#define AICA_RAM_START 0x030000 +#define AICA_RAM_END 0x200000 + +/* Quick access to the AICA channels */ +#define AICA_CHANNEL(x) (AICA_MEM_CHANNELS + (x) * sizeof(aica_channel_t)) + +#endif /* __ARM_AICA_CMD_IFACE_H */ diff --git a/engines/sci/dc/aica_crt0.s b/engines/sci/dc/aica_crt0.s new file mode 100644 index 0000000000..12415c0391 --- /dev/null +++ b/engines/sci/dc/aica_crt0.s @@ -0,0 +1,149 @@ +# +# Copyright 2000, 2001, 2002 +# Dan Potter. All rights reserved. +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of Cryptic Allusion nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +.text +.globl arm_main +.globl jps + +# Meaningless but makes the linker shut up +.globl reset +reset: + +# Exception vectors + b start + b undef + b softint + b pref_abort + b data_abort + b rsrvd + b irq + + +# FIQ code adapted from the Marcus AICA example +fiq: + # Save regs + #stmdb sp!, {r0-r14} + + # Grab interrupt type (store as parameter) + ldr r8,intreq + ldr r9,[r8] + and r9,r9,#7 + + # Timer interupt? + cmp r9,#2 + beq fiq_timer + + # Bus request? + cmp r9,#5 + beq fiq_busreq + + # Dunno -- ack and skip + b fiq_done + +fiq_busreq: + # Type 5 is bus request. Wait until the INTBusRequest register + # goes back from 0x100. + ldr r8,busreq_control +fiq_busreq_loop: + # This could probably be done more efficiently, but I'm + # no ARM assembly expert... + ldr r9,[r8] + and r9,r9,#0x100 + cmp r9,#0 + bne fiq_busreq_loop + + b fiq_done + +fiq_timer: + # Type 2 is timer interrupt. Increment timer variable. + # Update the next line to AICA_MEM_CLOCK if you change AICA_CMD_IFACE + mov r8,#0x21000 + ldr r9,[r8] + add r9,r9,#1 + str r9,[r8] + + # Request a new timer interrupt. We'll calculate the number + # put in here based on the "jps" (jiffies per second). + ldr r8, timer_control + mov r9,#256-(44100/4410) +# ldr r9,jps + str r9,[r8,#0x10] + mov r9,#0x40 + str r9,[r8,#0x24] +# b fiq_done + + # Return from interrupt +fiq_done: + + # Clear interrupt + ldr r8,intclr + mov r9,#1 + str r9,[r8] + str r9,[r8] + str r9,[r8] + str r9,[r8] + + # Restore regs and return + #ldmdb sp!, {r0-r14} + subs pc,r14,#4 + +intreq: + .long 0x00802d00 +intclr: + .long 0x00802d04 +timer_control: + .long 0x00802880 +busreq_control: + .long 0x00802808 +jps: + # 1000 jiffies per second + .long 256-(44100/1000) + + +start: + # Setup a basic stack, disable IRQ, enable FIQ + mov sp,#0xb000 + mrs r10,CPSR + orr r10,r10,#0x80 + bic r10,r10,#0x40 + msr CPSR_all,r10 + + # Call the main for the SPU + bl arm_main + + # Loop infinitely if we get here +here: b here + + +# Handlers we don't bother to catch +undef: +softint: + mov pc,r14 +pref_abort: +data_abort: +irq: +rsrvd: + sub pc,r14,#4 diff --git a/engines/sci/dc/aica_main.c b/engines/sci/dc/aica_main.c new file mode 100644 index 0000000000..f42904f0a2 --- /dev/null +++ b/engines/sci/dc/aica_main.c @@ -0,0 +1,207 @@ +/* + * Copyright 2000, 2001, 2002 + * Dan Potter. All rights reserved. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Cryptic Allusion nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* 2005-11-09 Modified by Walter van Niftrik. */ + +#include "aica_cmd_iface.h" +#include "aica.h" + +/****************** Timer *******************************************/ + +#define timer (*((volatile uint32 *)AICA_MEM_CLOCK)) + +void timer_wait(int jiffies) { + int fin = timer + jiffies; + while (timer <= fin) + ; +} + +/****************** Tiny Libc ***************************************/ + +#include + +void * memcpy(void *dest, const void *src, size_t count) { + unsigned char *tmp = (unsigned char *) dest; + unsigned char *s = (unsigned char *) src; + + while (count--) + *tmp++ = *s++; + + return dest; +} + +/****************** Main Program ************************************/ + +/* Our SH-4 interface (statically placed memory structures) */ +volatile aica_queue_t *q_cmd = (volatile aica_queue_t *)AICA_MEM_CMD_QUEUE; +volatile aica_queue_t *q_resp = (volatile aica_queue_t *)AICA_MEM_RESP_QUEUE; +volatile aica_channel_t *chans = (volatile aica_channel_t *)AICA_MEM_CHANNELS; + +/* Process a CHAN command */ +void process_chn(uint32 chn, aica_channel_t *chndat) { + switch(chndat->cmd & AICA_CH_CMD_MASK) { + case AICA_CH_CMD_NONE: + break; + case AICA_CH_CMD_START: + if (chndat->cmd & AICA_CH_START_SYNC) { + aica_sync_play(chn); + } else { + memcpy((void*)(chans+chn), chndat, sizeof(aica_channel_t)); + chans[chn].pos = 0; + aica_play(chn, chndat->cmd & AICA_CH_START_DELAY); + } + break; + case AICA_CH_CMD_STOP: + aica_stop(chn); + break; + case AICA_CH_CMD_UPDATE: + if (chndat->cmd & AICA_CH_UPDATE_SET_FREQ) { + chans[chn].freq = chndat->freq; + aica_freq(chn); + } + if (chndat->cmd & AICA_CH_UPDATE_SET_VOL) { + chans[chn].vol = chndat->vol; + aica_vol(chn); + } + if (chndat->cmd & AICA_CH_UPDATE_SET_PAN) { + chans[chn].pan = chndat->pan; + aica_pan(chn); + } + break; + default: + /* error */ + break; + } +} + +/* Process one packet of queue data */ +uint32 process_one(uint32 tail) { + uint32 pktdata[AICA_CMD_MAX_SIZE], *pdptr, size, i; + volatile uint32 * src; + aica_cmd_t * pkt; + + src = (volatile uint32 *)(q_cmd->data + tail); + pkt = (aica_cmd_t *)pktdata; + pdptr = pktdata; + + /* Get the size field */ + size = *src; + if (size > AICA_CMD_MAX_SIZE) + size = AICA_CMD_MAX_SIZE; + + /* Copy out the packet data */ + for (i=0; i= (q_cmd->data + q_cmd->size)) + src = (volatile uint32 *)q_cmd->data; + } + + /* Figure out what type of packet it is */ + switch (pkt->cmd) { + case AICA_CMD_NONE: + break; + case AICA_CMD_PING: + /* Not implemented yet */ + break; + case AICA_CMD_CHAN: + process_chn(pkt->cmd_id, (aica_channel_t *)pkt->cmd_data); + break; + case AICA_CMD_SYNC_CLOCK: + /* Reset our timer clock to zero */ + timer = 0; + break; + default: + /* error */ + break; + } + + return size; +} + +/* Look for an available request in the command queue; if one is there + then process it and move the tail pointer. */ +void process_cmd_queue() { + uint32 head, tail, tsloc, ts; + + /* Grab these values up front in case SH-4 changes head */ + head = q_cmd->head; + tail = q_cmd->tail; + + /* Do we have anything to process? */ + while (head != tail) { + /* Look at the next packet. If our clock isn't there yet, then + we won't process anything yet either. */ + tsloc = tail + offsetof(aica_cmd_t, timestamp); + if (tsloc >= q_cmd->size) + tsloc -= q_cmd->size; + ts = *((volatile uint32*)(q_cmd->data + tsloc)); + if (ts > 0 && ts >= timer) + return; + + /* Process it */ + ts = process_one(tail); + + /* Ok, skip over the packet */ + tail += ts * 4; + if (tail >= q_cmd->size) + tail -= q_cmd->size; + q_cmd->tail = tail; + } +} + +int arm_main() { + int i; + + /* Setup our queues */ + q_cmd->head = q_cmd->tail = 0; + q_cmd->data = AICA_MEM_CMD_QUEUE + sizeof(aica_queue_t); + q_cmd->size = AICA_MEM_RESP_QUEUE - q_cmd->data; + q_cmd->process_ok = 1; + q_cmd->valid = 1; + + q_resp->head = q_resp->tail = 0; + q_resp->data = AICA_MEM_RESP_QUEUE + sizeof(aica_queue_t); + q_resp->size = AICA_MEM_CHANNELS - q_resp->data; + q_resp->process_ok = 1; + q_resp->valid = 1; + + /* Initialize the AICA part of the SPU */ + aica_init(); + + /* Wait for a command */ + for( ; ; ) { + /* Update channel position counters */ + for (i=0; i<64; i++) + aica_get_pos(i); + + /* Check for a command */ + if (q_cmd->process_ok) + process_cmd_queue(); + + /* Little delay to prevent memory lock */ + timer_wait(10); + } +} diff --git a/engines/sci/dc/aica_sup.c b/engines/sci/dc/aica_sup.c new file mode 100644 index 0000000000..f99179702c --- /dev/null +++ b/engines/sci/dc/aica_sup.c @@ -0,0 +1,242 @@ +/* + * Copyright 2000, 2001, 2002 + * Dan Potter. All rights reserved. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Cryptic Allusion nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* 2005-11-09 Modified by Walter van Niftrik. */ + +#include "aica_cmd_iface.h" +#include "aica.h" + +extern volatile aica_channel_t *chans; + +void aica_init() { + int i, j; + + /* Initialize AICA channels */ + SNDREG32(0x2800) = 0x0000; + + for (i=0; i<64; i++) { + CHNREG32(i,0) = 0x8000; + for (j=4; j<0x80; j+=4) + CHNREG32(i, j) = 0; + CHNREG32(i,20) = 0x1f; + } + + SNDREG32(0x2800) = 0x000f; +} + +/* Translates a volume from linear form to logarithmic form (required by + the AICA chip */ +static int logs[] = { + 0, 15, 22, 27, 31, 35, 39, 42, 45, 47, 50, 52, 55, 57, 59, 61, + 63, 65, 67, 69, 71, 73, 74, 76, 78, 79, 81, 82, 84, 85, 87, 88, + 90, 91, 92, 94, 95, 96, 98, 99, 100, 102, 103, 104, 105, 106, + 108, 109, 110, 111, 112, 113, 114, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 146, 147, 148, 149, 150, 151, 152, 152, 153, 154, 155, 156, 156, + 157, 158, 159, 160, 160, 161, 162, 163, 164, 164, 165, 166, 167, + 167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, + 177, 178, 178, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, + 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194, + 195, 195, 196, 197, 197, 198, 199, 199, 200, 200, 201, 202, 202, + 203, 204, 204, 205, 205, 206, 207, 207, 208, 209, 209, 210, 210, + 211, 212, 212, 213, 213, 214, 215, 215, 216, 216, 217, 217, 218, + 219, 219, 220, 220, 221, 221, 222, 223, 223, 224, 224, 225, 225, + 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 232, 232, 233, + 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 239, 239, 240, + 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, + 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 254, 255 +}; + +static inline int calc_aica_vol(int x) { + return 0xff - logs[x & 0xff]; +} + +static inline int calc_aica_pan(int x) { + if (x == 0x80) + return 0; + else if (x < 0x80) { + return 0x10 | ((0x7f - x) >> 3); + } else { + return (x - 0x80) >> 3; + } +} + +/* Sets up a sound channel completely. This is generally good if you want + a quick and dirty way to play notes. If you want a more comprehensive + set of routines (more like PC wavetable cards) see below. + + ch is the channel to play on (0 - 63) + smpptr is the pointer to the sound data; if you're running off the + SH4, then this ought to be (ptr - 0xa0800000); otherwise it's just + ptr. Basically, it's an offset into sound ram. + mode is one of the mode constants (16 bit, 8 bit, ADPCM) + nsamp is the number of samples to play (not number of bytes!) + freq is the sampling rate of the sound + vol is the volume, 0 to 0xff (0xff is louder) + pan is a panning constant -- 0 is left, 128 is center, 255 is right. + + This routine (and the similar ones) owe a lot to Marcus' sound example -- + I hadn't gotten quite this far into dissecting the individual regs yet. */ +void aica_play(int ch, int delay) { + unsigned long smpptr = chans[ch].base; + int mode = chans[ch].type; + int loopst = chans[ch].loopstart; + int loopend = chans[ch].loopend; + int freq = chans[ch].freq; + int vol = chans[ch].vol; + int pan = chans[ch].pan; + int loopflag = chans[ch].loop; + unsigned long freq_lo, freq_base = 5644800; + int freq_hi = 7; + int i; + uint32 playCont; + + /* Stop the channel (if it's already playing) */ + aica_stop(ch); + + /* Need to convert frequency to floating point format + (freq_hi is exponent, freq_lo is mantissa) + Formula is freq = 44100*2^freq_hi*(1+freq_lo/1024) */ + while (freq < freq_base && freq_hi > -8) { + freq_base >>= 1; + --freq_hi; + } + freq_lo = (freq<<10) / freq_base; + + /* Envelope setup. The first of these is the loop point, + e.g., where the sample starts over when it loops. The second + is the loop end. This is the full length of the sample when + you are not looping, or the loop end point when you are (though + storing more than that is a waste of memory if you're not doing + volume enveloping). */ + CHNREG32(ch, 8) = loopst & 0xffff; + CHNREG32(ch, 12) = loopend & 0xffff; + + /* Write resulting values */ + CHNREG32(ch, 24) = (freq_hi << 11) | (freq_lo & 1023); + + /* Set volume, pan */ + CHNREG8(ch, 36) = calc_aica_pan(pan); + CHNREG8(ch, 37) = 0xf; + /* turn off Low Pass Filter (LPF) */ + CHNREG8(ch, 40) = 0x24; + /* Convert the incoming volume and pan into hardware values */ + /* Vol starts at zero so we can ramp */ + CHNREG8(ch, 41) = 0xff; + + /* If we supported volume envelopes (which we don't yet) then + this value would set that up. The top 4 bits determine the + envelope speed. f is the fastest, 1 is the slowest, and 0 + seems to be an invalid value and does weird things). The + default (below) sets it into normal mode (play and terminate/loop). + CHNREG32(ch, 16) = 0xf010; + */ + CHNREG32(ch, 16) = 0x1f; /* No volume envelope */ + + + /* Set sample format, buffer address, and looping control. If + 0x0200 mask is set on reg 0, the sample loops infinitely. If + it's not set, the sample plays once and terminates. We'll + also set the bits to start playback here. */ + CHNREG32(ch, 4) = smpptr & 0xffff; + playCont = (mode<<7) | (smpptr >> 16); + vol = calc_aica_vol(vol); + + if (loopflag) + playCont |= 0x0200; + + if (delay) { + CHNREG32(ch, 0) = playCont; /* key off */ + CHNREG8(ch, 41) = vol; + } else { + CHNREG32(ch, 0) = 0xc000 | playCont; /* key on */ + + /* ramp up the volume */ + for (i=0xff; i>=vol; i--) + CHNREG8(ch, 41) = i; + } +} + +/* Start sound on all channels specified by chmap bitmap */ +void aica_sync_play(uint32 chmap) { + int i = 0; + while (chmap) { + if (chmap & 0x1) + CHNREG32(i, 0) = CHNREG32(i, 0) | 0xc000; + i++; + chmap >>= 1; + } +} + +/* Stop the sound on a given channel */ +void aica_stop(int ch) { + CHNREG32(ch, 0) = (CHNREG32(ch, 0) & ~0x4000) | 0x8000; +} + + +/* The rest of these routines can change the channel in mid-stride so you + can do things like vibrato and panning effects. */ + +/* Set channel volume */ +void aica_vol(int ch) { + CHNREG8(ch, 41) = calc_aica_vol(chans[ch].vol); +} + +/* Set channel pan */ +void aica_pan(int ch) { + CHNREG8(ch, 36) = calc_aica_pan(chans[ch].pan); +} + +/* Set channel frequency */ +void aica_freq(int ch) { + int freq = chans[ch].freq; + unsigned long freq_lo, freq_base = 5644800; + int freq_hi = 7; + + while (freq < freq_base && freq_hi > -8) { + freq_base >>= 1; + freq_hi--; + } + freq_lo = (freq<<10) / freq_base; + CHNREG32(ch, 24) = (freq_hi << 11) | (freq_lo & 1023); +} + +/* Get channel position */ +int aica_get_pos(int ch) { + int i; + + /* Observe channel ch */ + SNDREG8(0x280d) = ch; + + /* Wait a while */ + for (i = 0; i < 20; i++); + + /* Update position counters */ + chans[ch].pos = SNDREG32(0x2814) & 0xffff; + + return chans[ch].pos; +} diff --git a/engines/sci/dc/bkg.c b/engines/sci/dc/bkg.c new file mode 100644 index 0000000000..66af9b78ab --- /dev/null +++ b/engines/sci/dc/bkg.c @@ -0,0 +1,133 @@ +/* + * Copyright 2000, 2001, 2002 + * Dan Potter. All rights reserved. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Cryptic Allusion nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "gp.h" + + +/* This module will manage the spiffy background effects */ + +static int ang = 0; +static pvr_ptr_t chktexture; +static float horizon = 340.0f; + +/* Make a nice (now familiar =) XOR pattern texture */ +void bkg_setup() { + int x, y; + uint16 *texture; + + chktexture = pvr_mem_malloc(64*64*2); + texture = (uint16*)chktexture; + for (y=0; y<64; y++) + for (x=0; x<64; x++) { + int v = ((x*4)^(y*4)) & 255; + *texture++ = ((v >> 3) << 11) + | ((v >> 2) << 5) + | ((v >> 3) << 0); + } +} + +/* Draws the floor polygon */ +static void draw_floor_poly() { + pvr_vertex_t vert; + float u2 = 0.374f, j = 0.0f; + + vert.flags = PVR_CMD_VERTEX; + vert.x = 0.0f; vert.y = 480.0f; vert.z = 64.0f; + vert.u = u2 - 0.5f; vert.v = 1.0f - 0.5f; + rotate(ang, 0, 0, &vert.u, &vert.v, &j); + vert.u += 0.5f; vert.v += 0.5f; + vert.argb = PVR_PACK_COLOR(1.0f, 0.2f, 1.0f, 1.0f); + vert.oargb = 0; + pvr_prim(&vert, sizeof(vert)); + + vert.x = 0.0f; vert.y = horizon; vert.z = 16.0f; + vert.u = 0.0f - 0.5f; vert.v = 0.0f - 0.5f; + rotate(ang, 0, 0, &vert.u, &vert.v, &j); + vert.u += 0.5f; vert.v += 0.5f; + vert.argb = PVR_PACK_COLOR(1.0f, 0.5f, 0.3f, 1.0f); + pvr_prim(&vert, sizeof(vert)); + + vert.x = 640.0f; vert.y = 480.0f; vert.z = 64.0f; + vert.u = (1.0f - u2) - 0.5f; vert.v = 1.0f - 0.5f; + rotate(ang, 0, 0, &vert.u, &vert.v, &j); + vert.u += 0.5f; vert.v += 0.5f; + vert.argb = PVR_PACK_COLOR(1.0f, 0.2f, 1.0f, 1.0f); + pvr_prim(&vert, sizeof(vert)); + + vert.flags = PVR_CMD_VERTEX_EOL; + vert.x = 640.0f; vert.y = horizon; vert.z = 16.0f; + vert.u = 1.0f - 0.5f; vert.v = 0.0f - 0.5f; + rotate(ang, 0, 0, &vert.u, &vert.v, &j); + vert.u += 0.5f; vert.v += 0.5f; + vert.argb = PVR_PACK_COLOR(1.0f, 0.5f, 0.3f, 1.0f); + pvr_prim(&vert, sizeof(vert)); +} + +/* Draws the "wall" polygon */ +static void draw_wall_poly() { + pvr_vertex_t vertc; + + vertc.flags = PVR_CMD_VERTEX; + vertc.x = 0.0f; vertc.y = horizon; vertc.z = 64.0f; + vertc.argb = PVR_PACK_COLOR(1.0f, 0.5f, 0.3f, 1.0f); + vertc.oargb = 0; + pvr_prim(&vertc, sizeof(vertc)); + + vertc.x = 0.0f; vertc.y = 0.0f; vertc.z = 64.0f; + vertc.argb = PVR_PACK_COLOR(1.0f, 0.0f, 0.0f, 0.0f); + pvr_prim(&vertc, sizeof(vertc)); + + vertc.x = 640.0f; vertc.y = horizon; vertc.z = 64.0f; + vertc.argb = PVR_PACK_COLOR(1.0f, 0.5f, 0.3f, 1.0f); + pvr_prim(&vertc, sizeof(vertc)); + + vertc.flags = PVR_CMD_VERTEX_EOL; + vertc.x = 640.0f; vertc.y = 0.0f; vertc.z = 64.0f; + vertc.argb = PVR_PACK_COLOR(1.0f, 0.0f, 0.0f, 0.0f); + pvr_prim(&vertc, sizeof(vertc)); +} + +void bkg_render() { + pvr_poly_cxt_t cxt; + pvr_poly_hdr_t poly; + + /* "floor" polygon */ + pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_RGB565 | PVR_TXRFMT_NONTWIDDLED, + 64, 64, chktexture, PVR_FILTER_BILINEAR); + pvr_poly_compile(&poly, &cxt); + pvr_prim(&poly, sizeof(poly)); + + draw_floor_poly(); + + /* "wall" polygon */ + pvr_poly_cxt_col(&cxt, PVR_LIST_OP_POLY); + pvr_poly_compile(&poly, &cxt); + pvr_prim(&poly, sizeof(poly)); + + draw_wall_poly(); + + ang = (ang-1) & 255; +} diff --git a/engines/sci/dc/config.h.in b/engines/sci/dc/config.h.in new file mode 100644 index 0000000000..9baad96096 --- /dev/null +++ b/engines/sci/dc/config.h.in @@ -0,0 +1,174 @@ +/* Define to 1 if you have the header file. */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DMEDIA_AUDIO_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DMEDIA_MIDI_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the `ffs' function. */ +/* #undef HAVE_FFS */ + +/* Define to 1 if you have the `fnmatch' function. */ +/* #undef HAVE_FNMATCH */ + +/* Define to 1 if you have the header file. */ +#define HAVE_FNMATCH_H 1 + +/* Define to 1 if you have the `fork' function. */ +/* #undef HAVE_FORK */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_GETOPT_H */ + +/* Define to 1 if you have the `getopt_long' function. */ +/* #undef HAVE_GETOPT_LONG */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_INTTYPES_H */ + +/* Define to 1 if you have the `isblank' function. */ +#define HAVE_ISBLANK 1 + +/* Define to 1 if you have the `asound' library (-lasound). */ +/* #undef HAVE_LIBASOUND */ + +/* Define to 1 if you have the `cygwin' library (-lcygwin). */ +/* #undef HAVE_LIBCYGWIN */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the `memchr' function. */ +#define HAVE_MEMCHR 1 + +/* Define to 1 if you have the `memfrob' function. */ +/* #undef HAVE_MEMFROB */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_MEMORY_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OBSTACK_H */ + +/* Define to 1 if you have the `pipe' function. */ +/* #undef HAVE_PIPE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_READLINE_HISTORY_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_READLINE_READLINE_H */ + +/* Define to 1 if you have the `sched_yield' function. */ +/* #undef HAVE_SCHED_YIELD */ + +/* Define to 1 if you have the `socketpair' function. */ +/* #undef HAVE_SOCKETPAIR */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_STDINT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_STRINGS_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SOUNDCARD_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_STAT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_UIO_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `usleep' function. */ +#define HAVE_USLEEP 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_X11_EXTENSIONS_XRENDER_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_X11_XFT_XFT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_XM_MWMUTIL_H */ + +/* Define if want to output debug info for every memory allocation */ +/* #undef MALLOC_DEBUG */ + +/* Defined if not compiling a sound server */ +/* #define NO_SOUND 1 */ + +/* Name of package */ +#define PACKAGE "freesci" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* The size of a `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* The size of a `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + +/* The size of a `short', as computed by sizeof. */ +#define SIZEOF_SHORT 2 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Defined if not checking memory allocations */ +/* #undef UNCHECKED_MALLOCS */ + +/* Define if using the dmalloc debugging malloc package */ +/* #undef WITH_DMALLOC */ + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +/* #undef WORDS_BIGENDIAN */ + +/* Define to 1 if the X Window System is missing or not being used. */ +#define X_DISPLAY_MISSING 1 + +/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a + `char[]'. */ +#define YYTEXT_POINTER 1 + +/* Define as `__inline' if that's what the C compiler calls it, or to nothing + if it is not supported. */ +/* #undef inline */ + +#define HAVE_UNLINK 1 +#define HAVE_RMDIR 1 +#define HAVE_GETTIMEOFDAY 1 diff --git a/engines/sci/dc/dc.h b/engines/sci/dc/dc.h new file mode 100644 index 0000000000..d80aee0f00 --- /dev/null +++ b/engines/sci/dc/dc.h @@ -0,0 +1,88 @@ +/*************************************************************************** + dc.h Copyright (C) 2002,2003 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +#ifndef __DC_H +#define __DC_H + +#include + +/* Functions implemented in dc_save.c */ + +/* Returns a string with the VFS path to the first VMU. +** Parameters: void. +** Returns : Pointer to VFS path string on success, NULL on error. +*/ +char *dc_get_first_vmu(); + +/* Constructs the save game filename as it'll go on the VMU. +** Parameters: (char *) game_name: Game id of the current game. +** (int) nr: The current save game number. +** Returns : Pointer to save game name. +*/ +char *dc_get_cat_name(char *game_name, int nr); + +/* Deletes all save game files from a directory. +** Parameters: (char *) dir: Path of the directory to delete the save game +** files from. +** Returns : void. +*/ +void dc_delete_save_files(char *dir); + +/* Deletes the temporary file which is used for constructing the save files. +** Parameters: void. +** Returns : void. +*/ +int dc_delete_temp_file(); + +/* Retrieves a save game from the first VMU and puts it on the ram disk. +** Parameters: (char *) game_name: Game id of the current game. +** (int) nr: The number of the save game to retrieve. +** Returns : 0 on success, -1 on error. +*/ +int dc_retrieve_savegame(char *game_name, int nr); + +/* Stores a save game from the ram disk on the first VMU. +** Parameters: (char *) game_name: Game id of the current game. +** (char *) desc: Description of the save game. +** (int) nr: The number of the save game to store. +** Returns : 0 on success, -1 on error. +*/ +int dc_store_savegame(char *game_name, char *desc, int nr); + +/* Retrieves the mirrored files from the first VMU to the ram disk. +** Parameters: (char *) game_name: Game id of the current game. +** Returns : 0 on success, -1 on error. +*/ +int dc_retrieve_mirrored(char *game_name); + +/* Stores the mirrored files from the ram disk on the first VMU. +** Parameters: (char *) game_name: Game id of the current game. +** Returns : 0 on success, -1 on error. +*/ +int dc_store_mirrored(char *game_name); + +#endif /* __DC_H */ diff --git a/engines/sci/dc/dc_save.c b/engines/sci/dc/dc_save.c new file mode 100644 index 0000000000..6982c5e39c --- /dev/null +++ b/engines/sci/dc/dc_save.c @@ -0,0 +1,548 @@ +/*************************************************************************** + dc_save.c Copyright (C) 2003 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +#include +#include +#include +#include +#include + +#define DC_TEMP_FILE "/ram/freesci.tmp" + +/* The file header that is put before each file during the concatenation */ +struct file_hdr_t { + char filename[32]; + int filesize; +}; + +int dc_delete_temp_file() { + if (fs_unlink(DC_TEMP_FILE)) { + sciprintf("%s, L%d: fs_unlink(\"" DC_TEMP_FILE "\") failed!\n", __FILE__, __LINE__); + return -1; + } + return 0; +} + +static int dc_cat_write(gzFile outf, char *infname) +/* Adds a fileheader and a file's data to a compressed file at the current +** file position. +** Parameters: (gzFile) outf: The compressed file to write to. +** (char *) infname: The path and name of the file to add. +** Returns : 0 on success, -1 on error. +*/ +{ + file_t inf; + char *buf, *name; + struct file_hdr_t file_hdr; + int rd; + + /* Determine file name */ + name = strrchr(infname, '/'); + if (name) name++; + else name = infname; + + if (strlen(name) > 31) { + sciprintf("%s, L%d: Filename `%s' too long!\n", __FILE__, __LINE__, name); + return -1; + } + + if (!(inf = fs_open(infname, O_RDONLY))) { + sciprintf("%s, L%d: fs_open(\"%s\", O_RDONLY) failed!\n", __FILE__, __LINE__, infname); + return -1; + } + + strcpy(file_hdr.filename, name); + file_hdr.filesize = fs_total(inf); + + if (gzwrite(outf, &file_hdr, sizeof(struct file_hdr_t)) < sizeof(struct file_hdr_t)) { + sciprintf("%s, L%d: gzwrite() failed!\n", __FILE__, __LINE__); + fs_close(inf); + return -1; + } + + buf = sci_malloc(1024); + + while ((rd = fs_read(inf, buf, 1024)) > 0) { + if (!(gzwrite(outf, buf, rd))) { + sciprintf("%s, L%d: gzwrite() failed!\n", __FILE__, __LINE__); + fs_close(inf); + sci_free(buf); + return -1; + } + } + + if (rd == -1) { + sciprintf("%s, L%d: fs_read() failed!\n", __FILE__, __LINE__); + fs_close(inf); + sci_free(buf); + return -1; + } + + fs_close(inf); + sci_free(buf); + + return 0; +} + +static int dc_cat_read(gzFile inf, char *outfdir) +/* Extracts a file from a concatenated compressed file. The current file +** position must point to a file header. +** Parameters: (gzFile) inf: The compressed file to read from. +** (char *) outfdir: The directory path where the file should be +** written. +** Returns : 0 on success, -1 on error. +*/ +{ + file_t outf; + char *buf, *outfname; + struct file_hdr_t file_hdr; + int rd; + + if (gzread(inf, &file_hdr, sizeof(struct file_hdr_t)) < sizeof(struct file_hdr_t)) { + sciprintf("%s, L%d: gzread() failed!\n", __FILE__, __LINE__); + return -1; + } + + /* Construct output filename */ + outfname = sci_malloc(strlen(file_hdr.filename)+strlen(outfdir)+2); + strcpy(outfname, outfdir); + strcat(outfname, "/"); + strcat(outfname, file_hdr.filename); + + if (!(outf = fs_open(outfname, O_WRONLY | O_TRUNC))) { + sciprintf("%s, L%d: fs_open(\"%s\", O_WRONLY | O_TRUNC) failed!\n", __FILE__, __LINE__, outfname); + sci_free(outfname); + return -1; + } + + sci_free(outfname); + buf = sci_malloc(1024); + + while (file_hdr.filesize > 0) { + rd = file_hdr.filesize; + if (rd > 1024) rd = 1024; + if (gzread(inf, buf, rd) < rd) { + sciprintf("%s, L%d: gzread() failed!\n", __FILE__, __LINE__); + fs_close(outf); + sci_free(buf); + return -1; + } + if (!(fs_write(outf, buf, rd))) { + sciprintf("%s, L%d: fs_write() failed!\n", __FILE__, __LINE__); + fs_close(outf); + sci_free(buf); + return -1; + } + file_hdr.filesize -= rd; + } + + fs_close(outf); + sci_free(buf); + + return 0; +} + +static int dc_is_save_file(char *fn) +/* Determines whether a filename is one of FreeSCI's filenames that are used +** for save ganes. +** Parameters: (char *) fn: The filename to consider. +** Returns : 1 when the filename is used for save games, 0 otherwise. +*/ +{ + return !fnmatch("state", fn, 0) || !fnmatch("heap", fn, 0) || + !fnmatch("hunk*", fn, 0) || !fnmatch("song.*", fn, 0) || + !fnmatch("sound", fn, 0) || !fnmatch("*.id", fn, 0); +} + +static int dc_package(char *infname, char *outdir, char *outname, char* desc) +/* Packages a file with a VMU header and stores it. +** Parameters: (char *) infname: The filename of the file to package. +** (char *) outdir: The directory path to write the packaged file. +** (char *) outname: The filename the packaged file should get. +** (char *) desc: The description of the file to put in the +** header. +** Returns : 0 on success, -1 on error. +*/ +{ + file_t inf, outf; + uint8 *data, *pkg_out; + vmu_pkg_t pkg; + int pkg_size; + sci_dir_t dirent; + char *olddir, *outfname; + + if (!(inf = fs_open(infname, O_RDONLY))) { + sciprintf("%s, L%d: fs_open(\"%s\", O_RDONLY) failed!\n", __FILE__, __LINE__, infname); + return -1; + } + if (!(data = fs_mmap(inf))) { + sciprintf("%s, L%d: fs_mmap() failed!\n", __FILE__, __LINE__); + fs_close(inf); + return -1; + } + strcpy(pkg.desc_short, "FreeSCI"); + strncpy(pkg.desc_long, desc, 31); + pkg.desc_long[31] = 0; + strcpy(pkg.app_id, "FreeSCI"); + pkg.icon_cnt = 0; + pkg.icon_anim_speed = 0; + pkg.eyecatch_type = VMUPKG_EC_NONE; + pkg.data = data; + pkg.data_len = fs_total(inf); + if (vmu_pkg_build(&pkg, &pkg_out, &pkg_size) < 0) { + sciprintf("%s, L%d: vmu_pkg_build() failed!\n", __FILE__, __LINE__); + fs_close(inf); + return -1; + } + + outfname = sci_malloc(strlen(outdir) + strlen(outname) + 2); + strcpy(outfname, outdir); + strcat(outfname, "/"); + strcat(outfname, outname); + + if (!(outf = fs_open(outfname, O_WRONLY | O_TRUNC))) { + sciprintf("%s, L%d: fs_open(\"%s\", O_WRONLY | O_TRUNC) failed!\n", __FILE__, __LINE__, outfname); + fs_close(inf); + free(outfname); + return -1; + } + free(outfname); + if (fs_write(outf, pkg_out, pkg_size) < pkg_size) { + sciprintf("%s, L%d: fs_write() failed!\n", __FILE__, __LINE__); + fs_close(inf); + fs_close(outf); + return -1; + } + fs_close(inf); + fs_close(outf); + + /* Check whether the save is actually written */ + + olddir = strdup(fs_getwd()); + fs_chdir(outdir); + sci_init_dir(&dirent); + if (!sci_find_first(&dirent, outname)) { + sciprintf("%s, L%d: Write to VMU failed!\n", __FILE__, __LINE__); + sci_finish_find(&dirent); + fs_chdir(olddir); + sci_free(olddir); + return -1; + } + sci_finish_find(&dirent); + fs_chdir(olddir); + sci_free(olddir); + return 0; +} + +static int dc_depackage(char *infname, char *outfname) +/* Depackages a file with a VMU header and stores it. +** Parameters: (char *) infname: The full path and filename of the file to +** depackage. +** (char *) outfname: The full path and filename of where to +** write the depackaged file. +** Returns : 0 on success, -1 on error. +*/ +{ + file_t inf, outf; + uint8 *data; + vmu_pkg_t pkg; + if (!(inf = fs_open(infname, O_RDONLY))) { + sciprintf("%s, L%d: fs_open(\"%s\", O_RDONLY) failed!\n", __FILE__, __LINE__, infname); + return -1; + } + if (!(data = fs_mmap(inf))) { + sciprintf("%s, L%d: fs_mmap() failed!\n", __FILE__, __LINE__); + fs_close(inf); + return -1; + } + if (vmu_pkg_parse(data, &pkg) == -1) { + sciprintf("%s, L%d: vmu_pkg_parse() failed!\n", __FILE__, __LINE__); + fs_close(inf); + return -1; + } + if (!(outf = fs_open(outfname, O_WRONLY | O_TRUNC))) { + sciprintf("%s, L%d: fs_open(\"%s\", O_WRONLY | O_TRUNC) failed!\n", __FILE__, __LINE__, outfname); + fs_close(inf); + return -1; + } + if (fs_write(outf, pkg.data, pkg.data_len) < pkg.data_len) { + sciprintf("%s, L%d: fs_write() failed!\n", __FILE__, __LINE__); + fs_close(inf); + fs_close(outf); + return -1; + } + fs_close(inf); + fs_close(outf); + return 0; +} + +static int store_files(char *src_dir, char *tar_dir, char *tar_name, char *desc, int mode) +/* Concatenates files from a directory and creates a package that can be put +** on a VMU. +** Parameters: (char *) src_dir: The full path of the directory that contains +** the files that should be stored. +** (char *) tar_dir: The full path of the directory where the +** resulting file should be placed. +** (char *) tar_name: The file name that the resulting file should +** get. +** (char *) desc: The description to use for the VMU header. +** (int) mode: 0: all files will be stored; 1: only FreeSCI save +** files will be stored. +** Returns : 0 on success, -1 on error. +*/ +{ + gzFile outf; + file_t d; + dirent_t *entry; + + if (!(d = fs_open(src_dir, O_RDONLY | O_DIR))) { + sciprintf("%s, L%d: fs_open(\"%s\", O_RDONLY | O_DIR) failed!\n", __FILE__, __LINE__, src_dir); + return -1; + } + + if (!(outf = gzopen(DC_TEMP_FILE, "w"))) { + sciprintf("%s, L%d: gzopen(\"" DC_TEMP_FILE "\", \"w\") failed!\n", __FILE__, __LINE__); + return -1; + } + + while ((entry = fs_readdir(d))) { + if ((mode == 1) && !dc_is_save_file(entry->name)) continue; + char *fn = sci_malloc(strlen(src_dir)+strlen(entry->name)+2); + strcpy(fn, src_dir); + strcat(fn, "/"); + strcat(fn, entry->name); + if (dc_cat_write(outf, fn)) { + sciprintf("%s, L%d: dc_cat_write() failed!\n", __FILE__, __LINE__); + sci_free(fn); + fs_close(d); + gzclose(outf); + dc_delete_temp_file(); + return -1; + } + sci_free(fn); + } + + fs_close(d); + + if (gzclose(outf) < 0) { + sciprintf("%s, L%d: gzclose() failed!\n", __FILE__, __LINE__); + return -1; + } + + if (dc_package(DC_TEMP_FILE, tar_dir, tar_name, desc)) { + sciprintf("%s, L%d: dc_package(\"" DC_TEMP_FILE "\", \"%s\", \"%s\", \"%s\") failed!\n", __FILE__, __LINE__, tar_dir, tar_name, desc); + return -1; + } + + return dc_delete_temp_file(); +} + +static int retrieve_files(char *fname, char *dir) +/* Extracts all files from a packaged concatenated file. +** Parameters: (char *) fname: The full path and filename of the concatenated +** packaged file. +** (char *) dir: The full path of the directory where the +** resulting files should be placed. +** Returns : 0 on success, -1 on error. +*/ +{ + gzFile inf; + if (dc_depackage(fname, DC_TEMP_FILE)) { + sciprintf("%s, L%d: dc_depackage(\"%s\", \"" DC_TEMP_FILE "\") failed!\n", __FILE__, __LINE__, fname); + return -1; + } + if (!(inf = gzopen(DC_TEMP_FILE, "r"))) { + sciprintf("%s, L%d: gzopen(\"" DC_TEMP_FILE "\", \"r\") failed!\n", __FILE__, __LINE__); + return -1; + } + while(!gzeof(inf)) dc_cat_read(inf, dir); + if (gzclose(inf) < 0) { + sciprintf("%s, L%d: gzclose() failed!\n", __FILE__, __LINE__); + return -1; + } + return dc_delete_temp_file(); +} + +void dc_delete_save_files(char *dir) { + file_t d; + dirent_t *entry; + + if (!(d = fs_open(dir, O_RDONLY | O_DIR))) { + sciprintf("%s, L%d: fs_open(\"%s\", O_RDONLY | O_DIR) failed!\n", __FILE__, __LINE__, dir); + return; + } + + while ((entry = fs_readdir(d))) { + char *fn = sci_malloc(strlen(dir)+strlen(entry->name)+2); + strcpy(fn, dir); + strcat(fn, "/"); + strcat(fn, entry->name); + if ((dc_is_save_file(entry->name)) && (fs_unlink(fn) < 0)) + sciprintf("%s, L%d: fs_unlink(\"%s\") failed!\n", __FILE__, __LINE__, fn); + sci_free(fn); + } + + fs_close(d); +} + +char *dc_get_cat_name(char *game_name, int nr) { + char suffices[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + char *save_name = sci_malloc(strlen(game_name) + 3); + strcpy(save_name, game_name); + save_name[strlen(game_name)] = '.'; + save_name[strlen(game_name) + 1] = suffices[nr]; + save_name[strlen(game_name) + 2] = 0; + return save_name; +} + +char *dc_get_first_vmu() { + sci_dir_t dirent; + char *olddir = strdup(fs_getwd()); + char *vmu; + char *retval = NULL; + fs_chdir("/vmu"); + sci_init_dir(&dirent); + vmu = sci_find_first(&dirent, "*"); + if (vmu) { + retval = sci_malloc(5 + strlen(vmu) + 1); + strcpy(retval, "/vmu/"); + strcat(retval, vmu); + } + sci_finish_find(&dirent); + fs_chdir(olddir); + sci_free(olddir); + return retval; +} + +int dc_retrieve_savegame(char *game_name, int nr) { + int retval = 0; + char *name, *fname, *vmu; + + dc_delete_save_files("/ram"); + + if (!(vmu = dc_get_first_vmu())) { + sciprintf("%s, L%d: No VMU found!\n", __FILE__, __LINE__); + return -1; + } + name = dc_get_cat_name(game_name, nr); + fname = sci_malloc(strlen(vmu) + strlen(name) + 2); + + strcpy(fname, vmu); + strcat(fname, "/"); + strcat(fname, name); + sci_free(name); + sci_free(vmu); + if (retrieve_files(fname, "/ram") < 0) { + sciprintf("%s, L%d: retrieve_files(\"%s\", \"/ram\") failed!\n", __FILE__, __LINE__, fname); + retval = -1; + } + sci_free(fname); + return retval; +} + +int dc_store_savegame(char *game_name, char *desc, int nr) { + char *vmu, *name; + int retval = 0; + + if (!(vmu = dc_get_first_vmu())) { + sciprintf("%s, L%d: No VMU found!\n", __FILE__, __LINE__); + return -1; + } + + name = dc_get_cat_name(game_name, nr); + + if (store_files("/ram", vmu, name, desc, 1) < 0) { + sciprintf("%s, L%d: store_files(\"/ram\", \"%s\", \"%s\", \"%s\") failed!\n", __FILE__, __LINE__, vmu, name, desc); + retval = -1; + } + + dc_delete_save_files("/ram"); + + sci_free(name); + sci_free(vmu); + + return retval; +} + +int dc_retrieve_mirrored(char *game_name) { + int retval = 0; + char *vmu, *fname; + + if (!(vmu = dc_get_first_vmu())) { + sciprintf("%s, L%d: No VMU found!\n", __FILE__, __LINE__); + return -1; + } + + fname = sci_malloc(strlen(vmu) + strlen(game_name) + 2); + + strcpy(fname, vmu); + strcat(fname, "/"); + strcat(fname, game_name); + sci_free(vmu); + if (retrieve_files(fname, "/ram") < 0) { + sciprintf("%s, L%d: retrieve_files(\"%s\", \"/ram\") failed!\n", __FILE__, __LINE__, fname); + retval = -1; + } + sci_free(fname); + return retval; +} + +int dc_store_mirrored(char *game_name) { + int retval = 0; + char *vmu; + sci_dir_t dirent; + char *olddir = strdup(fs_getwd()); + + fs_chdir("/ram"); + + sci_init_dir(&dirent); + if (!sci_find_first(&dirent, "*")) { + sciprintf("%s, L%d: No mirrored files found!\n", __FILE__, __LINE__); + sci_finish_find(&dirent); + fs_chdir(olddir); + sci_free(olddir); + return 0; + } + + sci_finish_find(&dirent); + fs_chdir(olddir); + sci_free(olddir); + + if (!(vmu = dc_get_first_vmu())) { + sciprintf("%s, L%d: No VMU found!\n", __FILE__, __LINE__); + return -1; + } + + if (store_files("/ram", vmu, game_name, "Configuration", 0) < 0) { + sciprintf("%s, L%d: store_files(\"/ram\", \"%s\", \"%s\", \"Configuration\") failed!\n", __FILE__, __LINE__, vmu, game_name); + retval = -1; + } + + sci_free(vmu); + + return retval; +} diff --git a/engines/sci/dc/gamemenu.c b/engines/sci/dc/gamemenu.c new file mode 100644 index 0000000000..ca96c6804f --- /dev/null +++ b/engines/sci/dc/gamemenu.c @@ -0,0 +1,702 @@ +/* + * Copyright 2000, 2001, 2002 + * Dan Potter. All rights reserved. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Cryptic Allusion nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Modified by Walter van Niftrik */ + +#include +#include +#include +#include "gp.h" +#include "options.h" +#include "dc.h" + +#define info_y 120.0f + +/* Game entries */ +typedef struct { + char fn[256]; /* Game name */ + char dir[256]; /* Full directory path */ +} game; + +char *freq_select[] = {"50Hz", "60Hz", "Test"}; + +/* Used to count the number of queued scenes. This is done to make sure that the +** image on the screen is current before calling a function which will block +** further scene rendering for a significant amount of time. +*/ +static int load_queued = 0; +/* Holds pointers to text strings describing the selected value of each option */ +static char* options_str[NUM_DC_OPTIONS]; +/* The index of the currently selected value of each option */ +static char options_nr[NUM_DC_OPTIONS]; +/* Array of games that were found */ +static game games[200]; +/* Number of games that were found */ +static int num_games = 0; +/* games array index of the currently selected game */ +static int sel_game = 0; +/* games array index of the game which is currently displayed first on the +** screen. +*/ +static int top_game = 0; +/* Number of options */ +static int num_options; +/* options_nr array index of the currently selected option */ +static int sel_option = 0; +/* options_nr array index of the game which is currently displayed first on the +** screen. +*/ +static int top_option = 0; +/* frequency selector index of the currently selected option */ +static int sel_freq = 1; +/* Number of frequency selector options. Always 3. */ +static int num_freq = 3; +/* freq_select array index of the option which is currently displayed first on +** the screen. Always 0. +*/ +static int top_freq = 0; + +/* Pointers to properties of the currently displayed data, either games list or +** options list. +*/ +static int *num_entries; +static int *selected; +static int *top; + +/* Counts frames. Used for delay purposes in the controller code */ +static int framecnt = 0; +/* Controls the color changes of the highlighting bar */ +static float throb = 0.2f, dthrob = 0.01f; + +/* Current state of menu: +** 0: Drive lid open. +** 1: Searching the CD for SCI games +** 2: Displaying game list +** 3: Running selected game +** 4: Displaying option list +** 5: Saving options to VMU +** 6: Waiting for CD lid to open +** 7: Waiting for Dreamcast to finish scanning the disc +** 8: Drive empty. +** 9: 50Hz/60Hz selector for PAL console +** 10: Testing 60Hz mode for 5 seconds +** +** State changes: 0->{7}, 1->{2,6}, 2->{0,3,4}, 4->{2,5}, 5->{2}, 6->{0} +** 7->{0,1,8}, 8->{0}, 9->{0,1,10}, 10->{9} +*/ +static int menu_state; + +/* Flag which indicates whether the options have been altered */ +static int save_flag = 0; + +static void menu_restart() +{ + int status; + + cdrom_get_status(&status, NULL); + + if ((status == CD_STATUS_PAUSED) || (status == CD_STATUS_STANDBY)) + menu_state = 1; + else + menu_state = 0; + + num_entries = &num_games; + top = &top_game; + selected = &sel_game; +} + +void init_menu_state() +{ + /* On PAL consoles without a VGA box, display the 50Hz/60Hz selector */ + if ((flashrom_get_region() == FLASHROM_REGION_EUROPE) && + vid_check_cable()) { + num_entries = &num_freq; + top = &top_freq; + selected = &sel_freq; + menu_state = 9; + return; + } + else menu_restart(); +} + +static int load_options(char *infname, char *options) +/* Loads the options from an option file and stores them in an array. +** Parameters: (char *) infname: Full path and filename of the option file. +** (char *) options: Pointer to the option array. +** Returns : 0 on success, -1 on error. +*/ +{ + file_t inf; + uint8 *data; + vmu_pkg_t pkg; + int j; + if (!(inf = fs_open(infname, O_RDONLY))) { + sciprintf("%s, L%d: fs_open(\"%s\", O_RDONLY) failed!\n", __FILE__, __LINE__, infname); + return -1; + } + if (!(data = fs_mmap(inf))) { + sciprintf("%s, L%d: fs_mmap() failed!\n", __FILE__, __LINE__); + fs_close(inf); + return -1; + } + if (vmu_pkg_parse(data, &pkg) == -1) { + sciprintf("%s, L%d: vmu_pkg_parse() failed!\n", __FILE__, __LINE__); + fs_close(inf); + return -1; + } + if ((pkg.data_len != NUM_DC_OPTIONS+2) || + (pkg.data[0] != DC_OPTIONS_TAG_MAJOR) || + (pkg.data[1] != DC_OPTIONS_TAG_MINOR)) { + sciprintf("%s, L%d: Option file version doesn't match!\n", + __FILE__, __LINE__); + fs_close(inf); + return -1; + } + for (j = 0; j < NUM_DC_OPTIONS; j++) + options[j] = pkg.data[j+2]; + + fs_close(inf); + return 0; +} + +static int save_options(char *outfile, char *options) +/* Saves the options from an array to an option file. +** Parameters: (char *) outfname: Full path and filename of the option file. +** (char *) options: Pointer to the option array. +** Returns : 0 on success, -1 on error. +*/ +{ + file_t outf; + uint8 *pkg_out; + vmu_pkg_t pkg; + int pkg_size; + uint8 data[NUM_DC_OPTIONS+2]; + + strcpy(pkg.desc_short, "FreeSCI"); + strcpy(pkg.desc_long, "Configuration"); + strcpy(pkg.app_id, "FreeSCI"); + data[0] = DC_OPTIONS_TAG_MAJOR; + data[1] = DC_OPTIONS_TAG_MINOR; + memcpy(data+2, options_nr, NUM_DC_OPTIONS); + pkg.icon_cnt = 0; + pkg.icon_anim_speed = 0; + pkg.eyecatch_type = VMUPKG_EC_NONE; + pkg.data = data; + pkg.data_len = NUM_DC_OPTIONS+2; + if (vmu_pkg_build(&pkg, &pkg_out, &pkg_size) < 0) { + sciprintf("%s, L%d: vmu_pkg_build() failed!\n", __FILE__, __LINE__); + return -1; + } + if (!(outf = fs_open(outfile, O_WRONLY | O_TRUNC))) { + sciprintf("%s, L%d: fs_open(\"%s\", O_WRONLY | O_TRUNC) failed!\n", __FILE__, __LINE__, outfile); + return -1; + } + if (fs_write(outf, pkg_out, pkg_size) < pkg_size) { + sciprintf("%s, L%d: fs_write() failed!\n", __FILE__, __LINE__); + fs_close(outf); + return -1; + } + fs_close(outf); + return 0; +} + +void load_option_list() { + int i; + char *vmu; + + /* Load defaults */ + for (i = 0; i < NUM_DC_OPTIONS; i++) + options_nr[i] = 0; + + /* Overwrite with data from option file */ + if ((vmu = dc_get_first_vmu())) { + char *fn = sci_malloc(strlen(vmu) + 9); + strcpy(fn, vmu); + strcat(fn, "/freesci"); + if (load_options(fn, &options_nr[0])) + sciprintf("%s, L%d: Loading options from VMU failed!\n", __FILE__, __LINE__); + sci_free(fn); + sci_free(vmu); + } + else sciprintf("%s, L%d: No VMU found!\n", __FILE__, __LINE__); + + /* Calculate strings */ + for (i = 0; i < NUM_DC_OPTIONS; i++) { + int j; + options_str[i] = dc_options[i].values; + for (j = 0; j < options_nr[i]; j++) + options_str[i] += strlen(options_str[i]) + 1; + } + + num_options = NUM_DC_OPTIONS; +} + +static void load_game_list(char *dir) +/* Scans a directory and it's subdirectories for SCI games and stores the name +** and directory of each game. +** Parameters: (char *) dir: Path of the directory tree to scan. +** Returns : void. +*/ +{ + file_t d; + + d = fs_open(dir, O_RDONLY | O_DIR); + if (!d) return; + { + dirent_t *de; + while ((de = fs_readdir(d)) && num_games < 200) { + if (!stricmp(de->name, "resource.map")) { + strncpy(games[num_games].fn, dir, 256); + strncpy(games[num_games].dir, dir, 256); + num_games++; + } + else if (de->size < 0) { + char *new_dir; + new_dir = malloc(strlen(dir)+strlen(de->name)+2); + strcpy(new_dir, dir); + strcat(new_dir, "/"); + strcat(new_dir, de->name); + load_game_list(new_dir); + free(new_dir); + } + } + } + fs_close(d); +} + +static void draw_options() +/* Draws the options and their current values on the screen. +** Parameters: void. +** Returns : void. +*/ +{ + float y = 92.0f + 28.0f; + int i, esel; + + draw_poly_strf_ctr(y, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, "Options"); + + y += 2*24.0f; + + /* Draw all the options */ + for (i=0; i<7 && (top_option+i) 10) { + if (*selected > 0) { + (*selected)--; + if (*selected < *top) { + *top = *selected; + } + } + up_moved = framecnt; + } + } + if (!(cond.buttons & CONT_DPAD_DOWN)) { + if ((framecnt - down_moved) > 10) { + if (*selected < (*num_entries - 1)) { + (*selected)++; + if (*selected >= (*top+7)) { + (*top)++; + } + } + down_moved = framecnt; + } + } + if (cond.ltrig > 0) { + if ((framecnt - up_moved) > 10) { + *selected -= 7; + + if (*selected < 0) *selected = 0; + if (*selected < *top) *top = *selected; + up_moved = framecnt; + } + } + if (cond.rtrig > 0) { + if ((framecnt - down_moved) > 10) { + *selected += 7; + if (*selected > (*num_entries - 1)) + *selected = *num_entries - 1; + if (*selected >= (*top+7)) + *top = *selected; + down_moved = framecnt; + } + } + + if (!(cond.buttons & CONT_A)) { + if ((framecnt - a_pressed) > 5) + { + if (menu_state == 2) + { + fs_chdir(games[*selected].dir); + menu_state = 3; + } + else if (menu_state == 4) + { + options_str[sel_option] += strlen(options_str[sel_option]) + 1; + if (*options_str[sel_option] == '\0') { + options_str[sel_option] = dc_options[sel_option].values; + options_nr[sel_option] = 0; + } + else options_nr[sel_option]++; + save_flag = 1; + } + else if (menu_state == 9) + { + if (sel_freq == 0) menu_restart(); + else if (sel_freq == 1) { + vid_set_mode(DM_640x480, PM_RGB565); + menu_restart(); + } + else if (sel_freq == 2) { + menu_state = 10; + } + } + } + a_pressed = framecnt; + } + + if (!(cond.buttons & CONT_B)) { + if ((framecnt - b_pressed) > 5) { + if (menu_state == 4) { + num_entries = &num_games; + top = &top_game; + selected = &sel_game; + if (save_flag) + menu_state = 5; + else menu_state = 2; + } + else if (menu_state != 9) { + num_entries = &num_options; + top = &top_option; + selected = &sel_option; + menu_state = 4; + } + } + b_pressed = framecnt; + } + return; +} + +void render_button_info() { + draw_poly_box(20.0f, 440.0f-96.0f+4, 640.0f-20.0f, 440.0f, 90.0, + 0.3f, 0.2f, 0.5f, 0.0f, 0.5f, 0.1f, 0.8f, 0.2f); + if ((menu_state != 4) && (menu_state != 5)) { + draw_poly_strf(30.0f,440.0f-96.0f+6+10.0f,100.0f,1.0f,1.0f,1.0f,1.0f,"D-PAD : Select game L : Page up"); + draw_poly_strf(30.0f,440.0f-96.0f+6+24.0f+10.0f,100.0f,1.0f,1.0f,1.0f,1.0f," A : Start game R : Page down"); + draw_poly_strf(30.0f,440.0f-96.0f+6+48.0f+10.0f,100.0f,1.0f,1.0f,1.0f,1.0f," B : Options"); + } + else { + draw_poly_strf(30.0f,440.0f-96.0f+6+10.0f,100.0f,1.0f,1.0f,1.0f,1.0f,"D-PAD : Select option L : Page up"); + draw_poly_strf(30.0f,440.0f-96.0f+6+24.0f+10.0f,100.0f,1.0f,1.0f,1.0f,1.0f," A : Change option R : Page down"); + draw_poly_strf(30.0f,440.0f-96.0f+6+48.0f+10.0f,100.0f,1.0f,1.0f,1.0f,1.0f," B : Back"); + } +} + +void render_scroll(float speed, float y, float z, float a, float r, float g, float b, char *s) +/* Renders a scrolling text. +** Parameters: void. +** Returns : void. +*/ +{ + static float coord = 640.0f; + draw_poly_strf(coord, y, z, a , r, g ,b, s); + coord -= speed; + if (coord < strlen(s)*-12.0f) + coord = 640.0f; +} + +int game_menu_render() { + /* Draw a background box */ + draw_poly_box(30.0f, 80.0f+28.0f, 610.0f, 440.0f-96.0f, 90.0f, + 0.2f, 0.8f, 0.5f, 0.0f, 0.2f, 0.8f, 0.8f, 0.2f); + + if (menu_state == 0) { + int status; + draw_poly_strf(32.0f, info_y, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, + "The drive lid is open."); + draw_poly_strf(32.0f, info_y+24.0f, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, + "Please insert a game cd."); + cdrom_get_status(&status, NULL); + if (status == CD_STATUS_BUSY) menu_state = 7; + return 0; + } + + else if (menu_state == 1) { + if (load_queued < 4) { + draw_poly_strf(32.0f, info_y, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, + "Searching cd for SCI games..."); + load_queued++; + return 0; + } else { + load_game_list("/cd"); + load_queued = 0; + if (num_games == 0) menu_state = 6; + else menu_state = 2; + return 0; + } + } + + else if (menu_state == 3) { + draw_poly_strf(32.0f, info_y, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, + "Starting game, please wait..."); + if (load_queued < 4) { + load_queued++; + return 0; + } else { + return 1; + } + } + + else if (menu_state == 4) { + /* Draw option menu */ + draw_options(); + } + + else if (menu_state == 2) { + int status; + + cdrom_get_status(&status, NULL); + if (status == CD_STATUS_OPEN) { + *num_entries = 0; + *selected = 0; + menu_state = 0; + return 0; + } + + /* Draw the game listing */ + draw_listing(); + } + + else if (menu_state == 5) { + if (load_queued < 4) { + draw_poly_strf(32.0f, info_y, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, + "Saving options, please wait..."); + load_queued++; + } + else { + char *vmu; + if ((vmu = dc_get_first_vmu())) { + char *fn = sci_malloc(strlen(vmu) + 9); + strcpy(fn, vmu); + strcat(fn, "/freesci"); + if (save_options(fn, &options_nr[0])) + sciprintf("%s, L%d: Saving options to VMU failed!\n", __FILE__, __LINE__); + sci_free(fn); + sci_free(vmu); + } + else sciprintf("%s, L%d: No VMU found!\n", __FILE__, __LINE__); + save_flag = 0; + load_queued = 0; + menu_state = 2; + } + return 0; + } + + else if (menu_state == 6) { + int status; + draw_poly_strf(32.0f, info_y, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, + "No SCI games found!"); + draw_poly_strf(32.0f, info_y + 24.0f, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, + "Please insert a game cd."); + cdrom_get_status(&status, NULL); + if (status == CD_STATUS_OPEN) menu_state = 0; + return 0; + } + + else if (menu_state == 7) { + int status; + draw_poly_strf(32.0f, info_y, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, + "Scanning cd..."); + cdrom_get_status(&status, NULL); + if (status == CD_STATUS_PAUSED) menu_state = 1; + else if (status == CD_STATUS_NO_DISC) menu_state = 8; + else if (status == CD_STATUS_OPEN) menu_state = 0; + return 0; + } + + else if (menu_state == 8) { + int status; + draw_poly_strf(32.0f, info_y, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, + "The drive is empty!"); + draw_poly_strf(32.0f, info_y + 24.0f, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, + "Please insert a game cd."); + cdrom_get_status(&status, NULL); + if (status == CD_STATUS_OPEN) menu_state = 0; + return 0; + } + else if (menu_state == 9) { + /* Draw frequency selector */ + draw_selector(); + } + else if (menu_state == 10) { + static int cnt = 60*5; + if (cnt == 60*5) vid_set_mode(DM_640x480, PM_RGB565); + if (--cnt) { + draw_poly_strf_ctr(info_y+3*24.0, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, + "Testing 60Hz Display Mode"); + draw_poly_strf_ctr(info_y+5*24.0f, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, + "%i", cnt/60+1); + } + else { + vid_set_mode(DM_640x480_PAL_IL, PM_RGB565); + cnt = 60*5; + menu_state = 9; + } + } + + /* Adjust the throbber */ + throb += dthrob; + if (throb < 0.2f || throb > 0.8f) { + dthrob = -dthrob; + throb += dthrob; + } + + /* Check controller input */ + check_controller(); + + framecnt++; + + return 0; +} + +int dc_write_config_file(char *fn) { + FILE *cfile; + if ((cfile = fopen(fn, "w"))) { + fprintf(cfile, "[game]\n"); + fprintf(cfile, "resource_dir = %s\n", games[*selected].dir); + fprintf(cfile, "gfx.dc.render_mode = %s\n", options_nr[0]? "pvr" : "vram"); + fprintf(cfile, "gfx.dc.refresh_rate = %s\n", sel_freq? "60Hz" : "50Hz"); + fprintf(cfile, "pic0_dither_mode = "); + switch (options_nr[1]) { + case 0: + fprintf(cfile, "dither256\n"); + break; + case 1: + fprintf(cfile, "flat\n"); + break; + case 2: + fprintf(cfile, "dither\n"); + } + + if (options_nr[2]) + fprintf(cfile, "pic_antialiasing = simple"); + + if (options_nr[3]) + fprintf(cfile, "version = %s\n", options_str[3]); + + if (options_nr[4]) + fprintf(cfile, "resource_version = %s\n", options_str[4]); + + if (options_nr[5]) + fprintf(cfile, "pic_port_bounds = \"0, 0, 320, 200\"\n"); + + fclose(cfile); + return 0; + } + sciprintf("%s, L%d: fopen(\"%s\", \"w\") failed!\n", __FILE__, __LINE__, fn); + return -1; +} diff --git a/engines/sci/dc/gp.h b/engines/sci/dc/gp.h new file mode 100644 index 0000000000..8734df11fd --- /dev/null +++ b/engines/sci/dc/gp.h @@ -0,0 +1,90 @@ +/* + * Copyright 2000, 2001, 2002 + * Dan Potter. All rights reserved. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Cryptic Allusion nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Modified by Walter van Niftrik */ + +#ifndef __GP_H +#define __GP_H + +#include +#include + +/* Floating-point Sin/Cos; 256 angles, -1.0 to 1.0 */ +#define msin(angle) fsin((angle) * 2 * M_PI / 256) +#define mcos(angle) fcos((angle) * 2 * M_PI / 256) + +/* bkg.c */ +void bkg_setup(); +void bkg_render(); + +/* texture.c */ +extern pvr_ptr_t util_texture; +extern pvr_poly_hdr_t util_txr_hdr; +void setup_util_texture(); + +/* 3dutils.c */ +void rotate(int zang, int xang, int yang, float *x, float *y, float *z); +void draw_poly_mouse(int ptrx, int ptry, float alpha); +void draw_poly_char(float x1, float y1, float z1, float a, float r, float g, float b, int c); +void draw_poly_strf(float x1, float y1, float z1, float a, float r, float g, float b, char *fmt, ...); +void draw_poly_strf_ctr(float y1, float z1, float a, float r, float g, float b, char *fmt, ...); +void draw_poly_box(float x1, float y1, float x2, float y2, float z, + float a1, float r1, float g1, float b1, + float a2, float r2, float g2, float b2); + +/* gamemenu.c */ +/* Renders the interface and handles controller input. +** Parameters: void. +** Returns : 1 when the user has chosen a game, 0 otherwise. +*/ +int game_menu_render(); + +/* Renders the currently applicable button info. +** Parameters: void. +** Returns : void. +*/ +void render_button_info(); + +/* Fills the option list with the defaults, and then overwrites +** them (possible partially) when an option file is found. +** Parameters: void. +** Returns : void. +*/ +void load_option_list(); + +/* Saves the currently set options to a file on the first VMU. +** Parameters: void. +** Returns : 0 on success, -1 on error. +*/ +int dc_write_config_file(); + +/* Initializes the menu state based on the current cdrom drive status. +** Parameters: void. +** Returns : void. +*/ +void init_menu_state(); + +#endif /* __GP_H */ diff --git a/engines/sci/dc/keyboard.c b/engines/sci/dc/keyboard.c new file mode 100644 index 0000000000..deb346c4a7 --- /dev/null +++ b/engines/sci/dc/keyboard.c @@ -0,0 +1,5077 @@ +/*************************************************************************** + keyboard.c Copyright (C) 2004 Walter van Niftrik + 2004 Ismail Khatib + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +#include +#include +#include "keyboard.h" + +#define CTRL (1<<0) +#define ALT (1<<1) + +/* RGB565 colour used to mark keys. */ +#define MARK_COLOUR 0xff00 + +/* Pointer to buffer where keyboard is to be drawn. */ +static guint16 *ptr; +/* Buffer line pitch in bytes. */ +static int pitch; + +/* Current position of cursor, keyboard map displayed and buckybits. */ +static int cursor_row, cursor_col, kbd, bucky; + +/* Number of keys in each row. */ +static int max_key[] = {23, 23, 17}; + +/* Key bounds. key_bounds[y][x] is the starting pixel column of the xth key on +** row y. key_bounds[y][x+1]-1 is the ending pixel column of that key. +*/ +static int key_bounds[3][25] = { + {1, 16, 34, 47, 60, 73, 86, 99, 112, 125, 138, 151, + 164, 177, 190, 203, 216, 229, 242, 255, 268, 281, 294, 307, 320}, + {1, 19, 37, 50, 63, 76, 89, 102, 115, 128, 141, 154, + 167, 180, 193, 203, 216, 229, 242, 255, 268, 281, 294, 307, 320}, + {1, 19, 35, 48, 61, 74, 87, 100, 113, 126, 139, 152, + 165, 178, 203, 281, 294, 307, 320} + }; + +/* Keyboard map. */ +static int key_map[3][3][25] = { + {{SCI_K_ESC, 9, 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', + '[', ']', SCI_K_BACKSPACE, '1', '2', '3', '4', '5', '-', + SCI_K_HOME, SCI_K_UP, SCI_K_PGUP}, + {0, 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', + '`', 0, '6', '7', '8', '9', '0', '=', SCI_K_LEFT, + SCI_K_CENTER, SCI_K_RIGHT}, + {0, 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', + SCI_K_ENTER, ' ', SCI_K_END, SCI_K_DOWN, SCI_K_PGDOWN}}, + + {{SCI_K_ESC, 0, SCI_K_F1, SCI_K_F2, SCI_K_F3, SCI_K_F4, SCI_K_F5, + SCI_K_F6, SCI_K_F7, SCI_K_F8, SCI_K_F9, SCI_K_F10, 0, 0, + SCI_K_BACKSPACE, 0, SCI_K_INSERT, SCI_K_HOME, SCI_K_PGUP, + 0, 0, SCI_K_HOME, SCI_K_UP, SCI_K_PGUP}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, SCI_K_DELETE, + SCI_K_END, SCI_K_PGDOWN, 0, 0, SCI_K_LEFT, SCI_K_CENTER, + SCI_K_RIGHT}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, SCI_K_ENTER, ' ', + SCI_K_END, SCI_K_DOWN, SCI_K_PGDOWN}}, + + {{SCI_K_ESC, 9, 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', + '[', ']', SCI_K_BACKSPACE, '1', '2', '3', '4', '5', '-', + SCI_K_HOME, SCI_K_UP, SCI_K_PGUP}, + {0, 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', + '`', 0, '6', '7', '8', '9', '0', '=', SCI_K_LEFT, + SCI_K_CENTER, SCI_K_RIGHT}, + {0, 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', + SCI_K_ENTER, ' ', SCI_K_END, SCI_K_DOWN, SCI_K_PGDOWN}}, + }; + +/* RGB565 palette used for keyboard images. */ +static guint16 kbd_pal[256] = {0xFFFF, 0x0000, 0x0000}; + +/* Keyboard images in 256-colour indexed mode. Based on images by +** Ismail Khatib +*/ +static unsigned char kbd_img[3][320*40] = { + {0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 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, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 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, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 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, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02}, + + { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 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, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 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, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x02, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 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, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 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, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 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, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 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, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 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, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02}, + + { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 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, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 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, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 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, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02} +}; + +static void colour_key(int row, int column, guint16 bgcol, guint16 fgcol) +/* Changes the colouring of a key (palette colours 0 and 1). +** Parameters: (int) row: row number of key to colour. +** (int) column: column number of key to colour. +** (guint16) bgcol: colour by which to replace palette colour 0. +** (guint16) fgcol: colour by which to replace palette colour 1. +** Returns : (void) +*/ +{ + int x, y; + + for (y = row * 13 + 1; y < (row + 1) * 13 + 1; y++) + for (x = key_bounds[row][column]; x < key_bounds[row][column+1]; x++) + if (kbd_img[kbd][y * 320 + x] == 0) + *(ptr + y * pitch + x) = bgcol; + else if (kbd_img[kbd][y * 320 + x] == 1) + *(ptr + y * pitch + x) = fgcol; +} + +static void mark_key(int row, int column) +/* Marks a key +** Parameters: (int) row: row number of key to mark. +** (int) column: column number of key to mark. +** Returns : (void) +*/ +{ + if (((row == 1) && (column == 0) && (bucky & CTRL)) + || ((row == 2) && (column == 1) && (bucky & ALT)) + || ((row == 1) && (column == 1) && (kbd == 1)) + || ((row == 2) && (column == 0) && (kbd == 2))) + colour_key(row, column, kbd_pal[1], MARK_COLOUR); + else + colour_key(row, column, MARK_COLOUR, kbd_pal[1]); + if ((row == 2) && (column == 13)) + colour_key(1, 14, MARK_COLOUR, kbd_pal[1]); +} + +static void unmark_key(int row, int column) +/* Unmarks a key +** Parameters: (int) row: row number of key to unmark. +** (int) column: column number of key to unmark. +** Returns : (void) +*/ +{ + if (((row == 1) && (column == 0) && (bucky & CTRL)) + || ((row == 2) && (column == 1) && (bucky & ALT)) + || ((row == 1) && (column == 1) && (kbd == 1)) + || ((row == 2) && (column == 0) && (kbd == 2))) + colour_key(row, column, kbd_pal[1], kbd_pal[0]); + else + colour_key(row, column, kbd_pal[0], kbd_pal[1]); + if ((row == 2) && (column == 13)) + colour_key(1, 14, kbd_pal[0], kbd_pal[1]); +} + +void vkbd_handle_input(int input) +{ + unmark_key(cursor_row, cursor_col); + + switch (input) { + case KBD_LEFT: + if (cursor_col > 0) + cursor_col--; + else + cursor_col = max_key[cursor_row]; + break; + case KBD_RIGHT: + if (cursor_col < max_key[cursor_row]) + cursor_col++; + else + cursor_col = 0; + break; + case KBD_DOWN: + cursor_row = (cursor_row + 1) % 3; + switch (cursor_row) { + case 2: + if ((cursor_col >= 15) && (cursor_col <= 20)) + cursor_col = 14; + else if (cursor_col >= 21) + cursor_col -= 6; + break; + case 0: + if (cursor_col == 14) + cursor_col = 17; + else if (cursor_col >= 15) + cursor_col += 6; + } + break; + case KBD_UP: + cursor_row = (cursor_row + 2) % 3; + switch (cursor_row) { + case 2: + if ((cursor_col >= 15) && (cursor_col <= 20)) + cursor_col = 14; + else if (cursor_col >= 21) + cursor_col -= 6; + break; + case 1: + if (cursor_col == 14) + cursor_col = 17; + else if (cursor_col >= 15) + cursor_col += 6; + } + } + if ((cursor_row == 1) && (cursor_col == 14)) { + cursor_col = 13; + cursor_row = 2; + } + mark_key(cursor_row, cursor_col); +} + +int vkbd_get_key(int *data, int *buckyb) +{ + static int prev_kbd; + if ((cursor_row == 2) && (cursor_col == 0)) { + if (kbd == 0) { + kbd = 2; + vkbd_draw(); + } + else if (kbd == 2) { + kbd = 0; + vkbd_draw(); + } + } + else if ((cursor_row == 1) && (cursor_col == 1)) { + if ((kbd == 0) || (kbd == 2)) { + prev_kbd = kbd; + kbd = 1; + vkbd_draw(); + } + else if (kbd == 1) { + kbd = prev_kbd; + vkbd_draw(); + } + } + else if ((cursor_row == 1) && (cursor_col == 0)) { + bucky ^= CTRL; + mark_key(1, 0); + } + else if ((cursor_row == 2) && (cursor_col == 1)) { + bucky ^= ALT; + mark_key(2, 1); + } + if ((cursor_row > 0) && (cursor_col < 2)) + return 0; + *data = key_map[kbd][cursor_row][cursor_col]; + *buckyb = (kbd == 2 ? SCI_EVM_LSHIFT : 0) + | (bucky & CTRL ? SCI_EVM_CTRL : 0) + | (bucky & ALT ? SCI_EVM_ALT : 0); + return 1; +} + +void vkbd_init(guint16 *p, int line_pitch) +{ + ptr = p; + pitch = line_pitch; +} + +void vkbd_draw() +{ + int i, j; + for (i = 0; i < 40; i++) + for (j = 0; j < 320; j++) + *(ptr + i * pitch + j) = kbd_pal[kbd_img[kbd][i*320+j]]; + if (bucky & CTRL) + unmark_key(1, 0); + if (bucky & ALT) + unmark_key(2, 1); + if (kbd == 1) + unmark_key(1, 1); + else if (kbd == 2) + unmark_key(2, 0); + mark_key(cursor_row, cursor_col); +} diff --git a/engines/sci/dc/keyboard.h b/engines/sci/dc/keyboard.h new file mode 100644 index 0000000000..568a50a731 --- /dev/null +++ b/engines/sci/dc/keyboard.h @@ -0,0 +1,67 @@ +/*************************************************************************** + keyboard.c Copyright (C) 2004 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +#ifndef __KEYBOARD_H +#define __KEYBOARD_H + +/* Virtual keyboard. Currently only 320x40, 16-bit colour. */ + +#include + +#define KBD_RIGHT 0 +#define KBD_LEFT 1 +#define KBD_UP 2 +#define KBD_DOWN 3 + +void vkbd_init(guint16 *ptr, int line_pitch); +/* Initialises the virtual keyboard +** Parameters: (guint16 *) ptr: buffer where the keyboard is to be drawn. +** (int) line_pitch: line pitch of buffer in bytes. +** Returns : (void) +*/ + +void vkbd_draw(void); +/* Draws the virtual keyboard in its current state. +** Parameters: (void) +** Returns : (void) +*/ + +void vkbd_handle_input(int input); +/* Handle cursor moves. Keyboard display is automatically updated. +** Parameters: (int) input: cursor move to handle (see #defines above). +** Returns : (void) +*/ + +int vkbd_get_key(int *data, int *buckyb); +/* Get keycode of currently selected key and buckstate. If a special key is +** selected (e.g. the shift key), it will be dealt with appropriately. +** Parameters: (int *) data: pointer to where keycode will be stored. +** (int *) bucky: pointer to where buckystate will be stored. +** Returns : (int) 1 when new data and bucky are available, 0 otherwise. +*/ + +#endif /* __KEYBOARD_H */ diff --git a/engines/sci/dc/mouse1.h b/engines/sci/dc/mouse1.h new file mode 100644 index 0000000000..6267da7be7 --- /dev/null +++ b/engines/sci/dc/mouse1.h @@ -0,0 +1,44 @@ +/* + * Copyright 2000, 2001, 2002 + * Dan Potter. All rights reserved. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Cryptic Allusion nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* XPM: 10x16 */ +static char * mouse1_xpm = +" . " +".+. " +".++. " +".+++. " +".++++. " +".+++++. " +".++++++. " +".+++++++. " +".++++++++." +".+++++... " +".++.++. " +".+. .++. " +" . .++. " +" .++. " +" .++. " +" .. "; diff --git a/engines/sci/dc/options.h b/engines/sci/dc/options.h new file mode 100644 index 0000000000..eabdcbaf2c --- /dev/null +++ b/engines/sci/dc/options.h @@ -0,0 +1,51 @@ +/*************************************************************************** + options.h Copyright (C) 2003-2004 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +#ifndef __OPTIONS_H +#define __OPTIONS_H + +struct dc_option_t { + char *name; /* Option name */ + char *values; /* Option values, each followed by `\0' */ +}; + +/* 0 = stable, 1 = glutton */ +#define DC_OPTIONS_TAG_MAJOR 1 +/* Increase after every modification to the dc_options struct. */ +#define DC_OPTIONS_TAG_MINOR 1 +#define NUM_DC_OPTIONS 6 + +struct dc_option_t dc_options[NUM_DC_OPTIONS] = { +/* 0 */ { "Video Mode", "Letterboxed\0Full-Screen\0" }, +/* 1 */ { "Dithering", "256 Color Dithering\0Interpolate\00016 Color Dithering\0" }, +/* 2 */ { "Antialiasing", "Off\0On\0" }, +/* 3 */ { "SCI Version", "Autodetect\0001.000.000\0001.000.200\0001.000.510\0" }, +/* 4 */ { "Resource Version", "Autodetect\0001\0002\0003\0004\0005\0006\0" }, +/* 5 */ { "Pic Port Bounds", "Default\0\"0, 0, 320, 200\"\0" } +}; + +#endif /* __OPTIONS_H */ diff --git a/engines/sci/dc/selectgame.c b/engines/sci/dc/selectgame.c new file mode 100644 index 0000000000..42d9e4efee --- /dev/null +++ b/engines/sci/dc/selectgame.c @@ -0,0 +1,156 @@ +/* + * Copyright 2000, 2001, 2002 + * Dan Potter, Thorsten Titze. All rights reserved. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Cryptic Allusion nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Modified by Walter van Niftrik */ + +#include +#include "gp.h" + +#ifdef HAVE_CONFIG_H +#include +#endif + +static char copyright[] = "FreeSCI " VERSION " Copyright (C) 1999, 2000-2003" + " Alex Angas, Rainer Canavan, Ruediger Hanke, Matt Hargett," + " Dmitry Jemerov, Christopher T. Lansdown, Sergey Lapin, Rickard Lind," + " Carl Muckenhoupt, Walter van Niftrik, Solomon Peachy," + " Christoph Reichenbach, Magnus Reftel, Lars Skovlund, Rink Springer" + " and Petr Vyhnak." + " This program is free software. You can copy and/or modify it freely" + " according to the terms of the GNU general public license, v2.0" + " or any later version, at your option." + " It comes with ABSOLUTELY NO WARRANTY."; + +static int mx = 320, my = 240; +static int lmx[5] = {320, 320, 320, 320, 320}, + lmy[5] = {240, 240, 240, 240, 240}; +static void mouse_render() +/* Renders the mouse pointer. +** Parameters: void. +** Returns : void. +*/ +{ + int i; + int atall = 0; + + MAPLE_FOREACH_BEGIN(MAPLE_FUNC_MOUSE, mouse_state_t, st) + atall = 1; + if (st->dx || st->dy) { + + mx += st->dx; + my += st->dy; + + if (mx < 0) mx = 0; + if (mx > 640) mx = 640; + if (my < 0) my = 0; + if (my > 480) my = 480; + + lmx[0] = mx; + lmy[0] = my; + } + MAPLE_FOREACH_END() + + if (atall) { + for (i=4; i>0; i--) { + lmx[i] = lmx[i-1]; + lmy[i] = lmy[i-1]; + } + + draw_poly_mouse(mx, my, 1.0f); + for (i=1; i<5; i++) + draw_poly_mouse(lmx[i], lmy[i], 0.8f * (5-i) / 6.0f); + } +} + +void choose_game() { + int fexit = 0; + + /* On PAL consoles without a VGA box, default to 50hz */ + if ((flashrom_get_region() == FLASHROM_REGION_EUROPE) && + vid_check_cable()) { + vid_set_mode(DM_640x480_PAL_IL, PM_RGB565); + } + + /* Do basic setup */ + pvr_init_defaults(); + + /* Setup the mouse/font texture */ + setup_util_texture(); + + /* Setup background display */ + bkg_setup(); + + init_menu_state(); + + load_option_list(); + + while (!fexit) { + pvr_wait_ready(); + pvr_scene_begin(); + pvr_list_begin(PVR_LIST_OP_POLY); + + /* Opaque list *************************************/ + bkg_render(); + + /* End of opaque list */ + pvr_list_finish(); + pvr_list_begin(PVR_LIST_TR_POLY); + + /* Translucent list ********************************/ + + /* Top Banner */ + draw_poly_box(0.0f, 10.0f, 640.0f, 20.0f+(24.0f*2.0f)+10.0f+28.0f, 90.0f, + 0.3f, 0.2f, 0.5f, 0.0f, 0.5f, 0.1f, 0.8f, 0.2f); + draw_poly_strf(308.0f - (8+sizeof(VERSION)-1)/2.0f*12.0f, 20.0f, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, + "FreeSCI " VERSION); + draw_poly_strf(308.0f - 18.0f/2.0f*12.0f, 48.0f, 100.0f, 1.0f, 1.0f, 1.0f, 1.0f, + "http://freesci.org"); + + /* Game menu */ + fexit = game_menu_render(); + + /* Button info */ + render_button_info(); + + /* Copyright scroll */ + render_scroll(2.0f, 76.0f, 100.0f, 1.0f, 0.7f, 0.7f, 1.0f, copyright); + + /* Render the mouse if they move it.. it doesn't do anything + but it's cool looking ^_^ */ + mouse_render(); + + /* End of translucent list */ + pvr_list_finish(); + + /* Finish the frame *******************************/ + pvr_scene_finish(); + + } + + dc_write_config_file("/ram/config"); + + return; +} diff --git a/engines/sci/dc/selectgame.h b/engines/sci/dc/selectgame.h new file mode 100644 index 0000000000..0b4053010f --- /dev/null +++ b/engines/sci/dc/selectgame.h @@ -0,0 +1,40 @@ +/*************************************************************************** + selectgame.h Copyright (C) 2002,2003 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +#ifndef __SELECTGAME_H +#define __SELECTGAME_H + +/* This function changes to the directory of the game that the user wants to +** run. Currently implemented by a Dreamcast-specific graphical interface based +** on Daniel Potter's GhettoPlay interface. It also creates a config file on +** the ram disk, based on options the user has set in the interface. +** Parameters: void. +** Returns : void. +*/ +void choose_game(void); + +#endif /* __SELECTGAME_H */ diff --git a/engines/sci/dc/sintab.h b/engines/sci/dc/sintab.h new file mode 100644 index 0000000000..aaaf3fc505 --- /dev/null +++ b/engines/sci/dc/sintab.h @@ -0,0 +1,73 @@ +/* + * Copyright 2000, 2001, 2002 + * Dan Potter. All rights reserved. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Cryptic Allusion nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __SINTAB_H +#define __SINTAB_H + +float sintab[] = { +0.000000, 0.024541, 0.049068, 0.073565, 0.098017, 0.122411, 0.146730, +0.170962, 0.195090, 0.219101, 0.242980, 0.266713, 0.290285, 0.313682, +0.336890, 0.359895, 0.382683, 0.405241, 0.427555, 0.449611, 0.471397, +0.492898, 0.514103, 0.534998, 0.555570, 0.575808, 0.595699, 0.615232, +0.634393, 0.653173, 0.671559, 0.689541, 0.707107, 0.724247, 0.740951, +0.757209, 0.773010, 0.788346, 0.803208, 0.817585, 0.831470, 0.844854, +0.857729, 0.870087, 0.881921, 0.893224, 0.903989, 0.914210, 0.923880, +0.932993, 0.941544, 0.949528, 0.956940, 0.963776, 0.970031, 0.975702, +0.980785, 0.985278, 0.989177, 0.992480, 0.995185, 0.997290, 0.998795, +0.999699, 1.000000, 0.999699, 0.998795, 0.997290, 0.995185, 0.992480, +0.989177, 0.985278, 0.980785, 0.975702, 0.970031, 0.963776, 0.956940, +0.949528, 0.941544, 0.932993, 0.923880, 0.914210, 0.903989, 0.893224, +0.881921, 0.870087, 0.857729, 0.844854, 0.831470, 0.817585, 0.803208, +0.788346, 0.773010, 0.757209, 0.740951, 0.724247, 0.707107, 0.689541, +0.671559, 0.653173, 0.634393, 0.615232, 0.595699, 0.575808, 0.555570, +0.534998, 0.514103, 0.492898, 0.471397, 0.449611, 0.427555, 0.405241, +0.382683, 0.359895, 0.336890, 0.313682, 0.290285, 0.266713, 0.242980, +0.219101, 0.195090, 0.170962, 0.146730, 0.122411, 0.098017, 0.073565, +0.049068, 0.024541, 0.000000, -0.024541, -0.049068, -0.073565, -0.098017, +-0.122411, -0.146730, -0.170962, -0.195090, -0.219101, -0.242980, +-0.266713, -0.290285, -0.313682, -0.336890, -0.359895, -0.382683, +-0.405241, -0.427555, -0.449611, -0.471397, -0.492898, -0.514103, +-0.534998, -0.555570, -0.575808, -0.595699, -0.615232, -0.634393, +-0.653173, -0.671559, -0.689541, -0.707107, -0.724247, -0.740951, +-0.757209, -0.773010, -0.788346, -0.803208, -0.817585, -0.831470, +-0.844854, -0.857729, -0.870087, -0.881921, -0.893224, -0.903989, +-0.914210, -0.923880, -0.932993, -0.941544, -0.949528, -0.956940, +-0.963776, -0.970031, -0.975702, -0.980785, -0.985278, -0.989177, +-0.992480, -0.995185, -0.997290, -0.998795, -0.999699, -1.000000, +-0.999699, -0.998795, -0.997290, -0.995185, -0.992480, -0.989177, +-0.985278, -0.980785, -0.975702, -0.970031, -0.963776, -0.956940, +-0.949528, -0.941544, -0.932993, -0.923880, -0.914210, -0.903989, +-0.893224, -0.881921, -0.870087, -0.857729, -0.844854, -0.831470, +-0.817585, -0.803208, -0.788346, -0.773010, -0.757209, -0.740951, +-0.724247, -0.707107, -0.689541, -0.671559, -0.653173, -0.634393, +-0.615232, -0.595699, -0.575808, -0.555570, -0.534998, -0.514103, +-0.492898, -0.471397, -0.449611, -0.427555, -0.405241, -0.382683, +-0.359895, -0.336890, -0.313682, -0.290285, -0.266713, -0.242980, +-0.219101, -0.195090, -0.170962, -0.146730, -0.122411, -0.098017, +-0.073565, -0.049068, -0.024541 }; + + +#endif /* __SINTAB_H */ diff --git a/engines/sci/dc/snd_stream.c b/engines/sci/dc/snd_stream.c new file mode 100644 index 0000000000..fcff141c2e --- /dev/null +++ b/engines/sci/dc/snd_stream.c @@ -0,0 +1,537 @@ +/* + * Copyright 2000, 2001, 2002, 2003, 2004 + * Dan Potter, Florian Schulze. All rights reserved. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Cryptic Allusion nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* 2005-11-09 Modified by Walter van Niftrik. */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include <../sound/arm/aica_cmd_iface.h> + +#include + +/* + +This module uses a nice circularly queued data stream in SPU RAM, which is +looped by a program running in the SPU itself. + +Basically the poll routine checks to see if a certain minimum amount of +data is available to the SPU to be played, and if not, we ask the user +routine for more sound data and load it up. That's about it. + +This version is capable of playing back N streams at once, with the limit +being available CPU time and channels. + +*/ + +typedef struct filter { + TAILQ_ENTRY(filter) lent; + snd_stream_filter_t func; + void * data; +} filter_t; + +/* Each of these represents an active streaming channel */ +typedef struct strchan { + // Which AICA channels are we using? + int ch[2]; + + // The last write position in the playing buffer + int last_write_pos; // = 0 + + // The buffer size allocated for this stream. + int buffer_size; // = 0x10000 + + // Stream data location in AICA RAM + uint32 spu_ram_sch[2]; + + // "Get data" callback; we'll call this any time we want to get + // another buffer of output data. + snd_stream_callback_t get_data; + + // Our list of filter callback functions for this stream + TAILQ_HEAD(filterlist, filter) filters; + + // Stereo/mono flag + int stereo; + + // Playback frequency + int frequency; + + /* Stream queueing is where we get everything ready to go but don't + actually start it playing until the signal (for music sync, etc) */ + int queueing; + + /* Have we been initialized yet? (and reserved a buffer, etc) */ + volatile int initted; +} strchan_t; + +// Our stream structs +static strchan_t streams[SND_STREAM_MAX] = { { { 0 } } }; + +// Separation buffers (for stereo) +int16 * sep_buffer[2] = { NULL, NULL }; + +/* the address of the sound ram from the SH4 side */ +#define SPU_RAM_BASE 0xa0800000 + +// Check an incoming handle +#define CHECK_HND(x) do { \ + assert( (x) >= 0 && (x) < SND_STREAM_MAX ); \ + assert( streams[(x)].initted ); \ +} while(0) + +/* Set "get data" callback */ +void snd_stream_set_callback(snd_stream_hnd_t hnd, snd_stream_callback_t cb) { + CHECK_HND(hnd); + streams[hnd].get_data = cb; +} + +void snd_stream_filter_add(snd_stream_hnd_t hnd, snd_stream_filter_t filtfunc, void * obj) { + filter_t * f; + + CHECK_HND(hnd); + + f = malloc(sizeof(filter_t)); + f->func = filtfunc; + f->data = obj; + TAILQ_INSERT_TAIL(&streams[hnd].filters, f, lent); +} + +void snd_stream_filter_remove(snd_stream_hnd_t hnd, snd_stream_filter_t filtfunc, void * obj) { + filter_t * f; + + CHECK_HND(hnd); + + TAILQ_FOREACH(f, &streams[hnd].filters, lent) { + if (f->func == filtfunc && f->data == obj) { + TAILQ_REMOVE(&streams[hnd].filters, f, lent); + free(f); + return; + } + } +} + +static void process_filters(snd_stream_hnd_t hnd, void **buffer, int *samplecnt) { + filter_t * f; + + TAILQ_FOREACH(f, &streams[hnd].filters, lent) { + f->func(hnd, f->data, streams[hnd].frequency, streams[hnd].stereo ? 2 : 1, buffer, samplecnt); + } +} + + +/* Performs stereo seperation for the two channels; this routine + has been optimized for the SH-4. */ +static void sep_data(void *buffer, int len, int stereo) { + register int16 *bufsrc, *bufdst; + register int x, y, cnt; + + if (stereo) { + bufsrc = (int16*)buffer; + bufdst = sep_buffer[0]; + x = 0; y = 0; cnt = len / 2; + do { + *bufdst = *bufsrc; + bufdst++; bufsrc+=2; cnt--; + } while (cnt > 0); + + bufsrc = (int16*)buffer; bufsrc++; + bufdst = sep_buffer[1]; + x = 1; y = 0; cnt = len / 2; + do { + *bufdst = *bufsrc; + bufdst++; bufsrc+=2; cnt--; + x+=2; y++; + } while (cnt > 0); + } else { + memcpy(sep_buffer[0], buffer, len); + memcpy(sep_buffer[1], buffer, len); + } +} + +/* Prefill buffers -- do this before calling start() */ +void snd_stream_prefill(snd_stream_hnd_t hnd) { + void *buf; + int got; + long secs, usecs; + sfx_timestamp_t timestamp; + + CHECK_HND(hnd); + + if (!streams[hnd].get_data) return; + + sci_gettime(&secs, &usecs); + timestamp = sfx_new_timestamp(secs, usecs, streams[hnd].frequency); + + /* Load first buffer */ + /* XXX Note: This will not work if the full data size is less than + buffer_size or buffer_size/2. */ + if (streams[hnd].stereo) + buf = streams[hnd].get_data(hnd, timestamp, streams[hnd].buffer_size*2, &got); + else + buf = streams[hnd].get_data(hnd, timestamp, streams[hnd].buffer_size, &got); + process_filters(hnd, &buf, &got); + sep_data(buf, streams[hnd].buffer_size, streams[hnd].stereo); + spu_memload( + streams[hnd].spu_ram_sch[0], (uint8*)sep_buffer[0], + streams[hnd].buffer_size); + spu_memload( + streams[hnd].spu_ram_sch[1], (uint8*)sep_buffer[1], + streams[hnd].buffer_size); + + streams[hnd].last_write_pos = 0; +} + +/* Initialize stream system */ +int snd_stream_init() { + /* Create stereo seperation buffers */ + if (!sep_buffer[0]) { + sep_buffer[0] = memalign(32, (SND_STREAM_BUFFER_MAX/2)); + sep_buffer[1] = memalign(32, (SND_STREAM_BUFFER_MAX/2)); + } + + /* Finish loading the stream driver */ + if (snd_init() < 0) { + dbglog(DBG_ERROR, "snd_stream_init(): snd_init() failed, giving up\n"); + return -1; + } + + return 0; +} + +snd_stream_hnd_t snd_stream_alloc(snd_stream_callback_t cb, int bufsize) { + int i, old; + snd_stream_hnd_t hnd; + + // Get an unused handle + hnd = -1; + old = irq_disable(); + for (i=0; icmd = AICA_CMD_CHAN; + cmd->timestamp = 0; + cmd->size = AICA_CMDSTR_CHANNEL_SIZE; + cmd->cmd_id = streams[hnd].ch[0]; + chan->cmd = AICA_CH_CMD_START | AICA_CH_START_DELAY; + chan->base = streams[hnd].spu_ram_sch[0]; + chan->type = AICA_SM_16BIT; + chan->length = (streams[hnd].buffer_size/2); + chan->loop = 1; + chan->loopstart = 0; + chan->loopend = (streams[hnd].buffer_size/2); + chan->freq = freq; + chan->vol = 255; + chan->pan = 0; + snd_sh4_to_aica(tmp, cmd->size); + + /* Channel 1 */ + cmd->cmd_id = streams[hnd].ch[1]; + chan->base = streams[hnd].spu_ram_sch[1]; + chan->pan = 255; + snd_sh4_to_aica(tmp, cmd->size); + + /* Start both channels simultaneously */ + cmd->cmd_id = (1 << streams[hnd].ch[0]) | + (1 << streams[hnd].ch[1]); + chan->cmd = AICA_CH_CMD_START | AICA_CH_START_SYNC; + snd_sh4_to_aica(tmp, cmd->size); + + /* Process the changes */ + if (!streams[hnd].queueing) + snd_sh4_to_aica_start(); +} + +/* Actually make it go (in queued mode) */ +void snd_stream_queue_go(snd_stream_hnd_t hnd) { + CHECK_HND(hnd); + snd_sh4_to_aica_start(); +} + +/* Stop streaming */ +void snd_stream_stop(snd_stream_hnd_t hnd) { + AICA_CMDSTR_CHANNEL(tmp, cmd, chan); + + CHECK_HND(hnd); + + if (!streams[hnd].get_data) return; + + /* Stop stream */ + /* Channel 0 */ + cmd->cmd = AICA_CMD_CHAN; + cmd->timestamp = 0; + cmd->size = AICA_CMDSTR_CHANNEL_SIZE; + cmd->cmd_id = streams[hnd].ch[0]; + chan->cmd = AICA_CH_CMD_STOP; + snd_sh4_to_aica(tmp, cmd->size); + + /* Channel 1 */ + cmd->cmd_id = streams[hnd].ch[1]; + snd_sh4_to_aica(tmp, AICA_CMDSTR_CHANNEL_SIZE); +} + +/* The DMA will chain to this to start the second DMA. */ +/* static uint32 dmadest, dmacnt; +static void dma_chain(ptr_t data) { + spu_dma_transfer(sep_buffer[1], dmadest, dmacnt, 0, NULL, 0); +} */ + +/* Poll streamer to load more data if neccessary */ +int snd_stream_poll(snd_stream_hnd_t hnd) { + uint32 ch0pos, ch1pos; + long secs, usecs; + sfx_timestamp_t timestamp; + int realbuffer; + int current_play_pos; + int needed_samples; + int distance; + int got_samples; + int old; + void *data; + + CHECK_HND(hnd); + + if (!streams[hnd].get_data) return -1; + + /* Get "real" buffer */ + ch0pos = g2_read_32(SPU_RAM_BASE + AICA_CHANNEL(streams[hnd].ch[0]) + offsetof(aica_channel_t, pos)); + ch1pos = g2_read_32(SPU_RAM_BASE + AICA_CHANNEL(streams[hnd].ch[1]) + offsetof(aica_channel_t, pos)); + + if (ch0pos >= (streams[hnd].buffer_size/2)) { + dbglog(DBG_ERROR, "snd_stream_poll: chan0(%d).pos = %ld (%08lx)\n", streams[hnd].ch[0], ch0pos, ch0pos); + return -1; + } + + realbuffer = !((ch0pos < (streams[hnd].buffer_size/4)) && (ch1pos < (streams[hnd].buffer_size/4))); + + current_play_pos = (ch0pos < ch1pos)?(ch0pos):(ch1pos); + + /* count just till the end of the buffer, so we don't have to + handle buffer wraps */ + old = irq_disable(); + if (streams[hnd].last_write_pos <= current_play_pos) { + needed_samples = current_play_pos - streams[hnd].last_write_pos; + distance = streams[hnd].buffer_size/2 - current_play_pos + streams[hnd].last_write_pos; + } else { + needed_samples = (streams[hnd].buffer_size/2) - streams[hnd].last_write_pos; + distance = streams[hnd].last_write_pos - current_play_pos; + } + + sci_gettime(&secs, &usecs); + irq_restore(old); + + timestamp = sfx_new_timestamp(secs, usecs, streams[hnd].frequency); + timestamp = sfx_timestamp_add(timestamp, distance); + + /* round it a little bit */ + needed_samples &= ~0x7ff; + /* printf("last_write_pos %6i, current_play_pos %6i, needed_samples %6i\n",last_write_pos,current_play_pos,needed_samples); */ + + if (needed_samples > 0) { + if (streams[hnd].stereo) { + data = streams[hnd].get_data(hnd, timestamp, needed_samples * 4, &got_samples); + process_filters(hnd, &data, &got_samples); + if (got_samples < needed_samples * 4) { + needed_samples = got_samples / 4; + if (needed_samples & 3) + needed_samples = (needed_samples + 4) & ~3; + } + } else { + data = streams[hnd].get_data(hnd, timestamp, needed_samples * 2, &got_samples); + process_filters(hnd, &data, &got_samples); + if (got_samples < needed_samples * 2) { + needed_samples = got_samples / 2; + if (needed_samples & 1) + needed_samples = (needed_samples + 2) & ~1; + } + } + if (data == NULL) { + /* Fill the "other" buffer with zeros */ + spu_memset(streams[hnd].spu_ram_sch[0] + (streams[hnd].last_write_pos * 2), 0, needed_samples * 2); + spu_memset(streams[hnd].spu_ram_sch[1] + (streams[hnd].last_write_pos * 2), 0, needed_samples * 2); + return -3; + } + + sep_data(data, needed_samples * 2, streams[hnd].stereo); + spu_memload(streams[hnd].spu_ram_sch[0] + (streams[hnd].last_write_pos * 2), (uint8*)sep_buffer[0], needed_samples * 2); + spu_memload(streams[hnd].spu_ram_sch[1] + (streams[hnd].last_write_pos * 2), (uint8*)sep_buffer[1], needed_samples * 2); + + // Second DMA will get started by the chain handler + /* dcache_flush_range(sep_buffer[0], needed_samples*2); + dcache_flush_range(sep_buffer[1], needed_samples*2); + dmadest = spu_ram_sch2 + (last_write_pos * 2); + dmacnt = needed_samples * 2; + spu_dma_transfer(sep_buffer[0], spu_ram_sch1 + (last_write_pos * 2), needed_samples * 2, + 0, dma_chain, 0); */ + + streams[hnd].last_write_pos += needed_samples; + if (streams[hnd].last_write_pos >= (streams[hnd].buffer_size/2)) + streams[hnd].last_write_pos -= (streams[hnd].buffer_size/2); + } + return 0; +} + +/* Set the volume on the streaming channels */ +void snd_stream_volume(snd_stream_hnd_t hnd, int vol) { + AICA_CMDSTR_CHANNEL(tmp, cmd, chan); + + CHECK_HND(hnd); + + cmd->cmd = AICA_CMD_CHAN; + cmd->timestamp = 0; + cmd->size = AICA_CMDSTR_CHANNEL_SIZE; + cmd->cmd_id = streams[hnd].ch[0]; + chan->cmd = AICA_CH_CMD_UPDATE | AICA_CH_UPDATE_SET_VOL; + chan->vol = vol; + snd_sh4_to_aica(tmp, cmd->size); + + cmd->cmd_id = streams[hnd].ch[1]; + snd_sh4_to_aica(tmp, cmd->size); +} diff --git a/engines/sci/dc/stream.h b/engines/sci/dc/stream.h new file mode 100644 index 0000000000..e0e9871c70 --- /dev/null +++ b/engines/sci/dc/stream.h @@ -0,0 +1,106 @@ +/* + * Copyright 2002, 2004 + * Dan Potter. All rights reserved. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Cryptic Allusion nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* 2005-11-09 Modified by Walter van Niftrik. */ + +#ifndef __DC_SOUND_STREAM_H +#define __DC_SOUND_STREAM_H + +#include +__BEGIN_DECLS + +#include +#include + +/* The maximum number of streams which can be allocated at once */ +#define SND_STREAM_MAX 4 + +/* The maximum buffer size for a stream */ +#define SND_STREAM_BUFFER_MAX 0x10000 + +/* A stream handle */ +typedef int snd_stream_hnd_t; + +/* An invalid stream handle */ +#define SND_STREAM_INVALID -1 + +/* Set "get data" callback */ +typedef void* (*snd_stream_callback_t)(snd_stream_hnd_t hnd, sfx_timestamp_t timestamp, int smp_req, int * smp_recv); +void snd_stream_set_callback(snd_stream_hnd_t hnd, snd_stream_callback_t cb); + +/* Add an effect filter to the sound stream chain. When the stream + buffer filler needs more data, it starts out by calling the initial + callback (set above). It then calls each function in the effect + filter chain, which can modify the buffer and the amount of data + available as well. Filters persist across multiple calls to _init() + but will be emptied by _shutdown(). */ +typedef void (*snd_stream_filter_t)(snd_stream_hnd_t hnd, void * obj, int hz, int channels, void **buffer, int *samplecnt); +void snd_stream_filter_add(snd_stream_hnd_t hnd, snd_stream_filter_t filtfunc, void * obj); + +/* Remove a filter added with the above function */ +void snd_stream_filter_remove(snd_stream_hnd_t hnd, snd_stream_filter_t filtfunc, void * obj); + +/* Prefill buffers -- do this before calling start() */ +void snd_stream_prefill(snd_stream_hnd_t hnd); + +/* Initialize stream system */ +int snd_stream_init(); + +/* Shut everything down and free mem */ +void snd_stream_shutdown(); + +/* Allocate and init a stream channel */ +snd_stream_hnd_t snd_stream_alloc(snd_stream_callback_t cb, int bufsize); + +/* Re-init a stream channel */ +int snd_stream_reinit(snd_stream_hnd_t hnd, snd_stream_callback_t cb); + +/* Destroy a stream channel */ +void snd_stream_destroy(snd_stream_hnd_t hnd); + +/* Enable / disable stream queueing */ +void snd_stream_queue_enable(snd_stream_hnd_t hnd); +void snd_stream_queue_disable(snd_stream_hnd_t hnd); + +/* Actually make it go (in queued mode) */ +void snd_stream_queue_go(snd_stream_hnd_t hnd); + +/* Start streaming */ +void snd_stream_start(snd_stream_hnd_t hnd, uint32 freq, int st); + +/* Stop streaming */ +void snd_stream_stop(snd_stream_hnd_t hnd); + +/* Poll streamer to load more data if neccessary */ +int snd_stream_poll(snd_stream_hnd_t hnd); + +/* Set the volume on the streaming channels */ +void snd_stream_volume(snd_stream_hnd_t hnd, int vol); + +__END_DECLS + +#endif /* __DC_SOUND_STREAM_H */ + diff --git a/engines/sci/dc/texture.c b/engines/sci/dc/texture.c new file mode 100644 index 0000000000..35363bc02b --- /dev/null +++ b/engines/sci/dc/texture.c @@ -0,0 +1,73 @@ +/* + * Copyright 2000, 2001, 2002 + * Dan Potter. All rights reserved. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Cryptic Allusion nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "gp.h" + +/* Creates the utility texture used for the font and mouse cursor. The + resulting texture will be 256x256. */ +#include "mouse1.h" +pvr_ptr_t util_texture; +pvr_poly_hdr_t util_txr_hdr; +void setup_util_texture() { + uint16 *vram; + int x, y; + pvr_poly_cxt_t cxt; + + util_texture = pvr_mem_malloc(256*256*2); + printf("util_texture at %08x\n", util_texture); + vram = (uint16 *)util_texture; + + /* First dump in the mouse cursor */ + for (y=0; y<16; y++) { + for (x=0; x<10; x++) { + if (mouse1_xpm[y*10+x] == '.') + *vram = 0xffff; + else if (mouse1_xpm[y*10+x] == '+') + *vram = 0xf000; + else + *vram = 0x0000; + vram++; + } + vram += 256 - 10; + } + + /* Now add the rest as ASCII characters */ + vram = (uint16 *)util_texture; + for (y=0; y<8; y++) { + for (x=0; x<16; x++) { + /* Skip the first (it's a mouse pointer) */ + if (x != 0 || y != 0) + bfont_draw(vram, 256, 0, y*16+x); + vram += 16; + } + vram += 23*256; + } + + /* Setup a polygon header for the util texture */ + pvr_poly_cxt_txr(&cxt, PVR_LIST_TR_POLY, PVR_TXRFMT_ARGB4444 | PVR_TXRFMT_NONTWIDDLED, + 256, 256, util_texture, PVR_FILTER_NONE); + pvr_poly_compile(&util_txr_hdr, &cxt); +} diff --git a/engines/sci/engine/Makefile.am b/engines/sci/engine/Makefile.am new file mode 100644 index 0000000000..682f11efa2 --- /dev/null +++ b/engines/sci/engine/Makefile.am @@ -0,0 +1,11 @@ +INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@ +EXTRA_DIST = cfsml.pl savegame.cfsml gc.h heap.h kernel_compat.h \ + kernel_types.h sci_graphics.h +noinst_LIBRARIES = libsciengine.a +libsciengine_a_SOURCES = savegame.c kernel.c kscripts.c klists.c scriptconsole.c \ + kfile.c kgraphics.c kmath.c kevent.c kstring.c kmenu.c \ + kmovement.c kpathing.c ksound.c vm.c game.c scriptdebug.c \ + said.y grammar.c seg_manager.c sys_strings.c gc.c message.c + +savegame.c: savegame.cfsml + cat savegame.cfsml | ./cfsml.pl -l @CFSML_FLAGS@ -f savegame.cfsml > savegame.c diff --git a/engines/sci/engine/cfsml.pl b/engines/sci/engine/cfsml.pl new file mode 100644 index 0000000000..f136ffce9a --- /dev/null +++ b/engines/sci/engine/cfsml.pl @@ -0,0 +1,1182 @@ +#! /usr/bin/env perl +# The C File Storage Meta Language "reference" implementation +# This implementation is supposed to conform to version +$version = "0.8.2"; +# of the spec. Please contact the maintainer if it doesn't. +# +# cfsml.pl Copyright (C) 1999, 2000, 2001 Christoph Reichenbach +# +# +# This program may be modified and copied freely according to the terms of +# the GNU general public license (GPL), as long as the above copyright +# notice and the licensing information contained herein are preserved. +# +# Please refer to www.gnu.org for licensing details. +# +# This work is provided AS IS, without warranty of any kind, expressed or +# implied, including but not limited to the warranties of merchantibility, +# noninfringement, and fitness for a specific purpose. The author will not +# be held liable for any damage caused by this work or derivatives of it. +# +# By using this source code, you agree to the licensing terms as stated +# above. +# +# +# Please contact the maintainer for bug reports or inquiries. +# +# Current Maintainer: +# +# Christoph Reichenbach (CJR) [jameson@linuxgames.com] +# +# +# Warning: This is still a "bit" messy. Sorry for that. +# + +#$debug = 1; + +$write_lines = "true"; +$source_file = "CFSML source file"; +$type_integer = "integer"; +$type_string = "string"; +$type_record = "RECORD"; +$type_pointer = "POINTER"; +$type_abspointer = "ABSPOINTER"; + +%types; # Contains all type bindings +%records; # Contains all record bindings + +$mode = undef; +while ($op = shift @ARGV) { + if ($mode eq undef) { + if ($op eq "-f") { + $mode = "fname"; + } elsif ($op eq "-l") { + $write_lines = undef; + } elsif ($op eq "-v") { + print "cfsml.pl, the CFSML code generator, version $version\n"; + print "This program is provided WITHOUT WARRANTY of any kind. It may be\n"; + print "copied and modified freely according to the terms of the GNU\n"; + print "General Public License.\n"; + exit(0); + } elsif ($op eq "-h") { + print "CFSML help:\n"; + print "Usage: cat source | cfsml.pl [-v] [-h] [-l] [-f ] > dest\n"; + print " -h : help\n"; + print " -v : print version\n"; + print " -l : disable line number printing in dest file\n"; + print " -f : specify file name for line number printing\n"; + exit(0); + } else { + die "Unknown option '$op'\n"; + } + } elsif ($mode eq "fname") { + $source_file = $op; + $mode = 0; + } else { + die "Invalid internal state '$mode'\n"; + } +} + +sub write_line_pp +# write_line_pp(int line_nr, bool input_file?) +{ + my $line_nr = shift; + my $_file = shift; + my $filename = "cfsml.pl"; + + if (_file) { + $filename = $source_file; + } + + if ($write_lines) { + print "#line $line_nr \"$filename\"\n"; + } +} + +sub create_string_functions + { + $firstline = __LINE__; + $firstline += 4; + write_line_pp($firstline, 0); + print <<'EOF'; + +#include /* We need va_lists */ +#include + +#ifdef CFSML_DEBUG_MALLOC +/* +#define free(p) dbg_sci_free(p) +#define malloc(s) dbg_sci_malloc(s) +#define calloc(n, s) dbg_sci_calloc(n, s) +#define realloc(p, s) dbg_sci_realloc(p, s) +*/ +#define free dbg_sci_free +#define malloc dbg_sci_malloc +#define calloc dbg_sci_calloc +#define realloc dbg_sci_realloc +#endif + +static void +_cfsml_error(char *fmt, ...) +{ + va_list argp; + + fprintf(stderr, "Error: "); + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); + +} + + +static struct _cfsml_pointer_refstruct { + struct _cfsml_pointer_refstruct *next; + void *ptr; +} *_cfsml_pointer_references = NULL; + +static struct _cfsml_pointer_refstruct **_cfsml_pointer_references_current = &_cfsml_pointer_references; + +static char *_cfsml_last_value_retreived = NULL; +static char *_cfsml_last_identifier_retreived = NULL; + +static void +_cfsml_free_pointer_references_recursively(struct _cfsml_pointer_refstruct *refs, int free_pointers) +{ + if (!refs) + return; + #ifdef CFSML_DEBUG_MALLOC + SCI_MEMTEST; + #endif + + _cfsml_free_pointer_references_recursively(refs->next, free_pointers); + #ifdef CFSML_DEBUG_MALLOC + SCI_MEMTEST; + + fprintf(stderr,"Freeing ptrref %p [%p] %s\n", refs->ptr, refs, free_pointers? + "ALL": "cleanup only"); + #endif + + if (free_pointers) + free(refs->ptr); + + #ifdef CFSML_DEBUG_MALLOC + SCI_MEMTEST; + #endif + free(refs); + #ifdef CFSML_DEBUG_MALLOC + SCI_MEMTEST; + #endif +} + +static void +_cfsml_free_pointer_references(struct _cfsml_pointer_refstruct **meta_ref, int free_pointers) +{ + _cfsml_free_pointer_references_recursively(*meta_ref, free_pointers); + *meta_ref = NULL; + _cfsml_pointer_references_current = meta_ref; +} + +static struct _cfsml_pointer_refstruct ** +_cfsml_get_current_refpointer() +{ + return _cfsml_pointer_references_current; +} + +static void _cfsml_register_pointer(void *ptr) +{ + struct _cfsml_pointer_refstruct *newref = (struct _cfsml_pointer_refstruct*)sci_malloc(sizeof (struct _cfsml_pointer_refstruct)); + #ifdef CFSML_DEBUG_MALLOC + SCI_MEMTEST; + fprintf(stderr,"Registering ptrref %p [%p]\n", ptr, newref); + #endif + newref->next = *_cfsml_pointer_references_current; + newref->ptr = ptr; + *_cfsml_pointer_references_current = newref; +} + + +static char * +_cfsml_mangle_string(char *s) +{ + char *source = s; + char c; + char *target = (char *) sci_malloc(1 + strlen(s) * 2); /* We will probably need less than that */ + char *writer = target; + + while ((c = *source++)) { + + if (c < 32) { /* Special character? */ + *writer++ = '\\'; /* Escape... */ + c += ('a' - 1); + } else if (c == '\\' || c == '"') + *writer++ = '\\'; /* Escape, but do not change */ + *writer++ = c; + + } + *writer = 0; /* Terminate string */ + + return (char *) sci_realloc(target, strlen(target) + 1); +} + + +static char * +_cfsml_unmangle_string(char *s) +{ + char *target = (char *) sci_malloc(1 + strlen(s)); + char *writer = target; + char *source = s; + char c; + + while ((c = *source++) && (c > 31)) { + if (c == '\\') { /* Escaped character? */ + c = *source++; + if ((c != '\\') && (c != '"')) /* Un-escape 0-31 only */ + c -= ('a' - 1); + } + *writer++ = c; + } + *writer = 0; /* Terminate string */ + + return (char *) sci_realloc(target, strlen(target) + 1); +} + + +static char * +_cfsml_get_identifier(FILE *fd, int *line, int *hiteof, int *assignment) +{ + int c; + int mem = 32; + int pos = 0; + int done = 0; + char *retval = (char *) sci_malloc(mem); + + if (_cfsml_last_identifier_retreived) { + free(_cfsml_last_identifier_retreived); + _cfsml_last_identifier_retreived = NULL; + } + + while (isspace(c = fgetc(fd)) && (c != EOF)); + if (c == EOF) { + _cfsml_error("Unexpected end of file at line %d\n", *line); + free(retval); + *hiteof = 1; + return NULL; + } + + ungetc(c, fd); + + while (((c = fgetc(fd)) != EOF) && ((pos == 0) || (c != '\n')) && (c != '=')) { + + if (pos == mem - 1) /* Need more memory? */ + retval = (char *) sci_realloc(retval, mem *= 2); + + if (!isspace(c)) { + if (done) { + _cfsml_error("Single word identifier expected at line %d\n", *line); + free(retval); + return NULL; + } + retval[pos++] = c; + } else + if (pos != 0) + done = 1; /* Finished the variable name */ + else if (c == '\n') + ++(*line); + } + + if (c == EOF) { + _cfsml_error("Unexpected end of file at line %d\n", *line); + free(retval); + *hiteof = 1; + return NULL; + } + + if (c == '\n') { + ++(*line); + if (assignment) + *assignment = 0; + } else + if (assignment) + *assignment = 1; + + if (pos == 0) { + _cfsml_error("Missing identifier in assignment at line %d\n", *line); + free(retval); + return NULL; + } + + if (pos == mem - 1) /* Need more memory? */ + retval = (char *) sci_realloc(retval, mem += 1); + + retval[pos] = 0; /* Terminate string */ +EOF + +if ($debug) { + print " printf(\"identifier is '%s'\\n\", retval);\n"; +} + + $firstline = __LINE__; + $firstline += 4; + write_line_pp($firstline, 0); + print <<'EOF2'; + + return _cfsml_last_identifier_retreived = retval; +} + + +static char * +_cfsml_get_value(FILE *fd, int *line, int *hiteof) +{ + int c; + int mem = 64; + int pos = 0; + char *retval = (char *) sci_malloc(mem); + + if (_cfsml_last_value_retreived) { + free(_cfsml_last_value_retreived); + _cfsml_last_value_retreived = NULL; + } + + while (((c = fgetc(fd)) != EOF) && (c != '\n')) { + + if (pos == mem - 1) /* Need more memory? */ + retval = (char *) sci_realloc(retval, mem *= 2); + + if (pos || (!isspace(c))) + retval[pos++] = c; + + } + + while ((pos > 0) && (isspace(retval[pos - 1]))) + --pos; /* Strip trailing whitespace */ + + if (c == EOF) + *hiteof = 1; + + if (pos == 0) { + _cfsml_error("Missing value in assignment at line %d\n", *line); + free(retval); + return NULL; + } + + if (c == '\n') + ++(*line); + + if (pos == mem - 1) /* Need more memory? */ + retval = (char *) sci_realloc(retval, mem += 1); + + retval[pos] = 0; /* Terminate string */ +EOF2 + + if ($debug) { + print " printf(\"value is '%s'\\n\", retval);\n"; + } + + $firstline = __LINE__; + $firstline += 4; + write_line_pp($firstline, 0); + print <<'EOF3'; + return (_cfsml_last_value_retreived = (char *) sci_realloc(retval, strlen(retval) + 1)); + /* Re-allocate; this value might be used for quite some while (if we are + ** restoring a string) + */ +} +EOF3 + } + + +# Call with $expression as a simple expression, like "tos + 1". +# Returns (in this case) ("tos", "-1"). +sub lvaluize + { + my @retval; +# print "//DEBUG: $expression ["; + my @tokens = split (/([+-\/\*])/, $expression); +# print join(",", @tokens); + $retval[0] = $tokens[0]; + + my $rightvalue = ""; + for ($i = 1; $tokens[$i]; $i++) { + + if ($tokens[$i] eq "+") { + $rightvalue .= "-"; + } elsif ($tokens[$i] eq "-") { + $rightvalue .= "+"; + } elsif ($tokens[$i] eq "/") { + $rightvalue .= "*"; + } elsif ($tokens[$i] eq "*") { + $rightvalue .= "/"; + } else { + $rightvalue .= $tokens[$i]; + } + } + + $retval[1] = $rightvalue; + +# print "] => ($retval[0];$retval[1])\n"; + + return @retval; + } + + + +sub create_declaration + { + $typename = $type; + $ctype = $types{$type}->{'ctype'}; + + if (not $types{$type}->{'external'}) { + $types{$type}{'writer'} = "_cfsml_write_" . $typename; + $types{$type}{'reader'} = "_cfsml_read_" . $typename; + write_line_pp(__LINE__, 0); + print "static void\n$types{$type}{'writer'}(FILE *fh, $ctype* save_struc);\n"; + print "static int\n$types{$type}{'reader'}(FILE *fh, $ctype* save_struc, char *lastval,". + " int *line, int *hiteof);\n\n"; + }; + + } + +sub create_writer + { + $typename = $type; + $ctype = $types{$type}{'ctype'}; + + write_line_pp(__LINE__, 0); + print "static void\n_cfsml_write_$typename(FILE *fh, $ctype* save_struc)\n{\n"; + if ($types{$type}{'type'} eq $type_record) { + print " int min, max, i;\n\n"; + } + + if ($types{$type}{'type'} eq $type_integer) { + print " fprintf(fh, \"%li\", (long) *save_struc);\n"; + } + elsif ($types{$type}{'type'} eq $type_string) { + write_line_pp(__LINE__, 0); + print " if (!(*save_struc))\n"; + print " fprintf(fh, \"\\\\null\\\\\");\n"; + print " else {\n"; + print " char *token = _cfsml_mangle_string((char *) *save_struc);\n"; + print " fprintf(fh, \"\\\"%s\\\"\", token);\n"; + print " free(token);\n"; + print " }\n"; + } + elsif ($types{$type}{'type'} eq $type_record) { + write_line_pp(__LINE__, 0); + print " fprintf(fh, \"{\\n\");\n"; + + for $n (@{$records{$type}}) { + + print " fprintf(fh, \"$n->{'name'} = \");\n"; + + if ($n->{'array'}) { # Check for arrays + + if ($n->{'array'} eq 'static' or $n->{'size'} * 2) { # fixed integer value? + print " min = max = $n->{'size'};\n"; + } + else { # No, a variable + print " min = max = save_struc->$n->{'size'};\n"; + } + + if ($n->{'maxwrite'}) { # A write limit? + print " if (save_struc->$n->{'maxwrite'} < min)\n"; + print " min = save_struc->$n->{'maxwrite'};\n"; + } + + if ($n->{'array'} eq 'dynamic') { + print " if (!save_struc->$n->{'name'})\n"; + print " min = max = 0; /* Don't write if it points to NULL */\n"; + } + + write_line_pp(__LINE__, 0); + print " fprintf(fh, \"[%d][\\n\", max);\n"; + print " for (i = 0; i < min; i++) {\n"; + print " $types{$n->{'type'}}{'writer'}"; + my $subscribstr = "[i]"; # To avoid perl interpolation problems + print "(fh, &(save_struc->$n->{'name'}$subscribstr));\n"; + print " fprintf(fh, \"\\n\");\n"; + print " }\n"; + print " fprintf(fh, \"]\");\n"; + + } elsif ($n->{'type'} eq $type_pointer) { # Relative pointer + + print " fprintf(fh, \"%d\", save_struc->$n->{'name'} - save_struc->$n->{'anchor'});" . + " /* Relative pointer */\n"; + + } elsif ($n->{'type'} eq $type_abspointer) { # Absolute pointer + + print " if (!save_struc->$n->{'name'})\n"; + print " fprintf(fh, \"\\\\null\\\\\");\n"; + print " else \n"; + print " $types{$n->{'reftype'}}{'writer'}"; + print "(fh, save_struc->$n->{'name'});\n"; + + } else { # Normal record entry + + print " $types{$n->{'type'}}{'writer'}"; + print "(fh, ($types{$n->{'type'}}{'ctype'}*) &(save_struc->$n->{'name'}));\n"; + + } + + print " fprintf(fh, \"\\n\");\n"; + } + + print " fprintf(fh, \"}\");\n"; + } + else { + print STDERR "Warning: Attempt to create_writer for invalid type '$types{$type}{'type'}'\n"; + } + print "}\n\n"; + + } + + +sub create_reader + { + $typename = $type; + $ctype = $types{$type}{'ctype'}; + + write_line_pp(__LINE__, 0); + print "static int\n_cfsml_read_$typename"; + print "(FILE *fh, $ctype* save_struc, char *lastval, int *line, int *hiteof)\n{\n"; + + print " char *token;\n"; + if ($types{$type}{'type'} eq $type_record) { + print "int min, max, i;\n"; + } + my $reladdress_nr = 0; # Number of relative addresses needed + my $reladdress = 0; # Current relative address number + my $reladdress_resolver = ""; # Relative addresses are resolved after the main while block + + if ($types{$type}{'type'} eq $type_record) { + + foreach $n (@{$records{$type}}) { # Count relative addresses we need + if ($n->{'type'} eq $type_pointer) { + ++$reladdress_nr; + } + } + + if ($reladdress_nr) { # Allocate stack space for all relative addresses needed + print " int reladdresses[$reladdress_nr] = {0};\n"; + } + } + + if ($types{$type}{'type'} eq $type_integer) { + write_line_pp(__LINE__, 0); + print "\n *save_struc = strtol(lastval, &token, 0);\n"; + print " if ( (*save_struc == 0) && (token == lastval) ) {\n"; + print " _cfsml_error(\"strtol failed at line %d\\n\", *line);\n"; + print " return CFSML_FAILURE;\n"; + print " }\n"; + print " if (*token != 0) {\n"; + print " _cfsml_error(\"Non-integer encountered while parsing int value at line %d\\n\","; + print " *line);\n"; + print " return CFSML_FAILURE;\n"; + print " }\n"; + print " return CFSML_SUCCESS;\n"; + } elsif ($types{$type}{'type'} eq $type_string) { + write_line_pp(__LINE__, 0); + print "\n"; + print " if (strcmp(lastval, \"\\\\null\\\\\")) { /* null pointer? */\n"; + print " if (*lastval == '\"') { /* Quoted string? */\n"; + print " int seeker = strlen(lastval);\n\n"; + print " while (lastval[seeker] != '\"')\n"; + print " --seeker;\n\n"; + print " if (!seeker) { /* No matching double-quotes? */\n"; + print " _cfsml_error(\"Unbalanced quotes at line %d\\n\", *line);\n"; + print " return CFSML_FAILURE;\n"; + print " }\n\n"; + print " lastval[seeker] = 0; /* Terminate string at closing quotes... */\n"; + print " lastval++; /* ...and skip the opening quotes locally */\n"; + print " }\n"; + print " *save_struc = _cfsml_unmangle_string(lastval);\n"; + print " _cfsml_register_pointer(*save_struc);\n"; + print " return CFSML_SUCCESS;\n"; + print " } else {\n"; + print " *save_struc = NULL;\n"; + print " return CFSML_SUCCESS;\n"; + print " }\n"; + } elsif ($types{$type}{'type'} eq $type_record) { + write_line_pp(__LINE__, 0); + print " int assignment, closed, done;\n\n"; + print " if (strcmp(lastval, \"{\")) {\n"; + print " _cfsml_error(\"Reading record $type; expected opening braces in line %d, got \\\"%s\\\"\\n\","; + print "*line, lastval);\n"; + print " return CFSML_FAILURE;\n"; + print " };\n"; + print " closed = 0;\n"; + print " do {\n"; + print " char *value;\n"; + print " token = _cfsml_get_identifier(fh, line, hiteof, &assignment);\n\n"; + print " if (!token) {\n"; + print " _cfsml_error(\"Expected token at line %d\\n\", *line);\n"; + print " return CFSML_FAILURE;\n"; + print " }\n"; + print " if (!assignment) {\n"; + print " if (!strcmp(token, \"}\")) \n"; + print " closed = 1;\n"; + print " else {\n"; + print " _cfsml_error(\"Expected assignment or closing braces in line %d\\n\", *line);\n"; + print " return CFSML_FAILURE;\n"; + print " }\n"; + print " } else {\n"; + print " value = \"\";\n"; + print " while (!value || !strcmp(value, \"\"))\n"; + print " value = _cfsml_get_value(fh, line, hiteof);\n"; + print " if (!value) {\n"; + print " _cfsml_error(\"Expected token at line %d\\n\", *line);\n"; + print " return CFSML_FAILURE;\n"; + print " }\n"; +# print " }\n"; + + + foreach $n (@{$records{$type}}) { # Now take care of all record elements + + my $type = $n->{'type'}; + my $reference = undef; + if ($type eq $type_abspointer) { + $reference = 1; + $type = $n->{'reftype'}; + } + my $name = $n->{'name'}; + my $reader = $types{$type}{'reader'}; + my $size = $n->{'size'}; + + print " if (!strcmp(token, \"$name\")) {\n"; + + if ($type eq $type_pointer) { # A relative pointer + + $reader = $types{'int'}{'reader'}; # Read relpointer as int + + write_line_pp(__LINE__, 0); + print " if ($reader(fh, &(reladdresses[$reladdress]), value, line, hiteof)) {\n"; + print " _cfsml_error(\"Expected token at line %d\\n\", *line);\n"; + print " return CFSML_FAILURE;\n"; + print " }\n"; + + # Make sure that the resulting variable is interpreted correctly + $reladdress_resolver .= " save_struc->$n->{'name'} =". + " save_struc->$n->{'anchor'} + reladdresses[$reladdress];\n"; + + ++$reladdress; # Prepare reladdress for next element + + } elsif ($n->{'array'}) { # Is it an array? + write_line_pp(__LINE__, 0); + print " if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) {\n"; + # The value must end with [, since we're starting array data, and it must also + # begin with [, since this is either the only character in the line, or it starts + # the "amount of memory to allocate" block + print " _cfsml_error(\"Opening brackets expected at line %d\\n\", *line);\n"; + print " return CFSML_FAILURE;\n"; + print " }\n"; + + if ($n->{'array'} eq 'dynamic') { + write_line_pp(__LINE__, 0); + # We need to allocate the array first + print " /* Prepare to restore dynamic array */\n"; + # Read amount of memory to allocate + print " max = strtol(value + 1, NULL, 0);\n"; + print " if (max < 0) {\n"; + print " _cfsml_error(\"Invalid number of elements to allocate for dynamic "; + print "array '%s' at line %d\\n\", token, *line);\n"; + print " return CFSML_FAILURE;\n"; + print " }\n\n"; + + print " if (max) {\n"; + print " save_struc->$name = ($n->{'type'} *) sci_malloc(max * sizeof($type));\n"; + print "#ifdef SATISFY_PURIFY\n"; + print " memset(save_struc->$name, 0, max * sizeof($type));\n"; + print "#endif\n"; + print " _cfsml_register_pointer(save_struc->$name);\n"; + print " }\n"; + print " else\n"; + print " save_struc->$name = NULL;\n" + + } else { # static array + print " /* Prepare to restore static array */\n"; + print " max = $size;\n"; + } + + write_line_pp(__LINE__, 0); + print " done = i = 0;\n"; + print " do {\n"; + if ($type eq $type_record) { + print " if (!(value = _cfsml_get_value(fh, line, hiteof))) {\n"; + } else { + print " if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) {\n"; + } + write_line_pp(__LINE__, 0); + + print " _cfsml_error(\"Token expected at line %d\\n\", *line);\n"; + print " return 1;\n"; + print " }\n"; + print " if (strcmp(value, \"]\")) {\n"; + print " if (i == max) {\n"; + print " _cfsml_error(\"More elements than space available (%d) in '%s' at "; + print "line %d\\n\", max, token, *line);\n"; + print " return CFSML_FAILURE;\n"; + print " }\n"; + my $helper = "[i++]"; + print " if ($reader(fh, &(save_struc->$name$helper), value, line, hiteof)) {\n"; + print " _cfsml_error(\"Token expected by $reader() for $name$helper at line %d\\n\", *line);\n"; + print " return CFSML_FAILURE;\n"; + print " }\n"; + print " } else done = 1;\n"; + print " } while (!done);\n"; + + if ($n->{'array'} eq "dynamic") { + my @xpr = lvaluize($expression = $n->{'size'}); + print " save_struc->$xpr[0] = max $xpr[1]; /* Set array size accordingly */\n"; + } + + if ($n->{'maxwrite'}) { + my @xpr = lvaluize($expression = $n->{'maxwrite'}); + print " save_struc->$xpr[0] = i $xpr[1]; /* Set number of elements */\n"; + } + + } + elsif ($reference) { + write_line_pp(__LINE__, 0); + print " if (strcmp(value, \"\\\\null\\\\\")) { /* null pointer? */\n"; + print " save_struc->$name = sci_malloc(sizeof ($type));\n"; + print " _cfsml_register_pointer(save_struc->$name);\n"; + print " if ($reader(fh, save_struc->$name, value, line, hiteof)) {\n"; + print " _cfsml_error(\"Token expected by $reader() for $name at line %d\\n\", *line);\n"; + print " return CFSML_FAILURE;\n"; + print " }\n"; + print " } else save_struc->$name = NULL;\n"; + } + else { # It's a simple variable or a struct + write_line_pp(__LINE__, 0); + print " if ($reader(fh, ($types{$type}{'ctype'}*) &(save_struc->$name), value, line, hiteof)) {\n"; + print " _cfsml_error(\"Token expected by $reader() for $name at line %d\\n\", *line);\n"; + print " return CFSML_FAILURE;\n"; + print " }\n"; + } + print " } else\n"; + + } + write_line_pp(__LINE__, 0); + print " {\n"; + print " _cfsml_error(\"$type: Assignment to invalid identifier '%s' in line %d\\n\","; + print " token, *line);\n"; + print " return CFSML_FAILURE;\n"; + print " }\n"; + print " }\n"; + + print " } while (!closed); /* Until closing braces are hit */\n"; + + print $reladdress_resolver; # Resolves any relative addresses + + print " return CFSML_SUCCESS;\n"; + } else { + print STDERR "Warning: Attempt to create_reader for invalid type '$types{$type}{'type'}'\n"; + } + + print "}\n\n"; + } + +# Built-in types + +%types = ( + 'int' => { + 'type' => $type_integer, + 'ctype' => "int", + }, + + 'string' => { + 'type' => $type_string, + 'ctype' => "char *", + }, + ); + + + +sub create_function_block { + print "\n/* Auto-generated CFSML declaration and function block */\n\n"; + write_line_pp(__LINE__, 0); + print "#define CFSML_SUCCESS 0\n"; + print "#define CFSML_FAILURE 1\n\n"; + create_string_functions; + + foreach $n ( keys %types ) { + create_declaration($type = $n); + } + + foreach $n ( keys %types ) { + if (not $types{$n}->{'external'}) { + create_writer($type = $n); + create_reader($type = $n); + } + } + print "\n/* Auto-generated CFSML declaration and function block ends here */\n"; + print "/* Auto-generation performed by cfsml.pl $version */\n"; +} + + +# Gnerates code to read a data type +# Parameters: $type: Type to read +# $datap: Pointer to the write destination +# $fh: Existing filehandle of an open file to use +# $eofvar: Variable to store _cfsml_eof into +sub insert_reader_code { + print "/* Auto-generated CFSML data reader code */\n"; + write_line_pp(__LINE__, 0); + print " {\n"; + if (!$linecounter) { + write_line_pp(__LINE__, 0); + print " int _cfsml_line_ctr = 0;\n"; + $linecounter = '_cfsml_line_ctr'; + } + if ($atomic) { + write_line_pp(__LINE__, 0); + print " struct _cfsml_pointer_refstruct **_cfsml_myptrrefptr = _cfsml_get_current_refpointer();\n"; + } + write_line_pp(__LINE__, 0); + print " int _cfsml_eof = 0, _cfsml_error;\n"; + print " int dummy;\n"; + + if ($firsttoken) { + write_line_pp(__LINE__, 0); + print " char *_cfsml_inp = $firsttoken;\n"; + } else { + write_line_pp(__LINE__, 0); + print " char *_cfsml_inp =". + " _cfsml_get_identifier($fh, &($linecounter), &_cfsml_eof, &dummy);\n\n"; + } + + write_line_pp(__LINE__, 0); + print " _cfsml_error =". + " $types{$type}{'reader'}($fh, $datap, _cfsml_inp, &($linecounter), &_cfsml_eof);\n"; + + if ($eofvar) { + write_line_pp(__LINE__, 0); + print " $eofvar = _cfsml_error;\n"; + } + if ($atomic) { + write_line_pp(__LINE__, 0); + print " _cfsml_free_pointer_references(_cfsml_myptrrefptr, _cfsml_error);\n"; + } + write_line_pp(__LINE__, 0); + print " if (_cfsml_last_value_retreived) {\n"; + print " free(_cfsml_last_value_retreived);\n"; + print " _cfsml_last_value_retreived = NULL;\n"; + print " }\n"; + print " if (_cfsml_last_identifier_retreived) {\n"; + print " free(_cfsml_last_identifier_retreived);\n"; + print " _cfsml_last_identifier_retreived = NULL;\n"; + print " }\n"; + print " }\n"; + print "/* End of auto-generated CFSML data reader code */\n"; +} + +# Generates code to write a data type +# Parameters: $type: Type to write +# $datap: Pointer to the write destination +# $fh: Existing filehandle of an open file to use +sub insert_writer_code { + write_line_pp(__LINE__, 0); + print "/* Auto-generated CFSML data writer code */\n"; + print " $types{$type}{'writer'}($fh, $datap);\n"; + print " fprintf($fh, \"\\n\");\n"; + print "/* End of auto-generated CFSML data writer code */\n"; +} + + +################ +# Main program # +################ + +$parsing = 0; +$struct = undef; # Not working on a struct +$commentmode = undef; +$line = 0; + +while () { + + $line++; + + if ($parsing) { + ($data) = split "#"; # Remove shell-style comments + @_ = ($data); + + s/\/\*.*\*\///g; # Remove C-style one-line comments + + ($data) = split "\/\/"; # Remove C++-style comments + @_ = ($data); + + if ($commentmode) { + + if (grep /\*\//, $_) { + ($empty, $_) = split /\*\//; + } else { + @_ = (); # Empty line + } + + } else { + if (grep /\/\*/, $_) { + $commentmode = 1; + ($_) = split /\/\*/; + } + } + + + # Now tokenize: + s/;//; + split /(\".*\"|[,\[\]\(\)\{\}])|\s+/; + + @items = @_; + + @tokens = (); + + $tokens_nr = 0; + for ($n = 0; $n < scalar @items; $n++) { # Get rid of all undefs + if ($_[$n]) { + $_ = $items[$n]; + s/\"//g; + $tokens[$tokens_nr++] = $_; + } + } + + # Now all tokens are in @tokens, and we have $tokens_nr of them. + +# print "//DEBUG: " . join ("|", @tokens) . "\n"; + + if ($tokens_nr) { + if ($tokens_nr == 2 && $tokens[0] eq "%END" && $tokens[1] eq "CFSML") { + + $struct && die "Record $struct needs closing braces in intput file (line $line)."; + + $parsing = 0; + create_function_block; + my $linep = $line + 1; + write_line_pp($linep, 1); + } elsif ($struct) { # Parsing struct + if ($tokens_nr == 1) { + if ($tokens[0] eq "}") { + $struct = undef; + } else { die "Invalid declaration of $token[0] in input file (line $line)\n";}; + } else { # Must be a member declaration + + my @structrecs = (@{$records{$struct}}); + my $newidx = (scalar @structrecs) or "0"; + my %member = (); + $member{'name'} = $tokens[1]; + $member{'type'} = $tokens[0]; + + if ($tokens_nr == 3 && $tokens[1] == "*") { + $tokens_nr = 2; + $member{'name'} = $tokens[2]; + $member{'reftype'} = $tokens[0]; + $member{'type'} = $type_abspointer; + } + + if ($tokens_nr == 4 and $tokens[0] eq $type_pointer) { # Relative pointer + + if (not $tokens[2] eq "RELATIVETO") { + die "Invalid relative pointer declaration in input file (line $line)\n"; + } + + $member{'anchor'} = $tokens[3]; # RelPointer anchor + + } else { # Non-pointer + + if (not $types{$tokens[0]}) { + die "Unknown type $tokens[0] used in input file (line $line)\n"; + } + + if ($tokens_nr > 2) { # Array + + if ($tokens[2] ne "[") { + die "Invalid token '$tokens[2]' in input file (line $line)\n"; + } + + $member{'array'} = "static"; + + if ($tokens[$tokens_nr - 1] ne "]") { + die "Array declaration incorrectly terminated in input file (line $line)\n"; + } + + $parsepos = 3; + + while ($parsepos < $tokens_nr) { + + if ($tokens[$parsepos] eq ",") { + + $parsepos++; + + } elsif ($tokens[$parsepos] eq "STATIC") { + + $member{'array'} = "static"; + $parsepos++; + + } elsif ($tokens[$parsepos] eq "DYNAMIC") { + + $member{'array'} = "dynamic"; + $parsepos++; + + } elsif ($tokens[$parsepos] eq "MAXWRITE") { + + $member{'maxwrite'} = $tokens[$parsepos + 1]; + $parsepos += 2; + + } elsif ($tokens[$parsepos] eq "]") { + + $parsepos++; + if ($parsepos != $tokens_nr) { + die "Error: Invalid tokens after array declaration in input file (line $line)\n"; + + } + } else { + + if ($member{'size'}) { + die "Attempt to use more than one array size in input file (line $line)\n" . + "(Original size was \"$member->{'size'}\", new size is \"$tokens[$parsepos]\"\n"; + } + + $member{'size'} = $tokens[$parsepos]; + $parsepos++; + } + } + + + unless ($member{'size'}) { + die "Array declaration without size in input file (line $line)\n"; + } + } + } + + @{$records{$struct}}->[$newidx] = \%member; + } + } else { # not parsing struct; normal operation. + + if ($tokens[0] eq "TYPE") { # Simple type declaration + + my $newtype = $tokens[1]; + + $types{$newtype}->{'ctype'} = $tokens[2]; + + if ($tokens_nr == 5) { # must be ...LIKE... + + unless ($tokens[3] eq "LIKE") { + die "Invalid TYPE declaration in input file (line $line)\n"; + } + + $types{$newtype}->{'type'} = $types{$tokens[4]}->{'type'}; + $types{$newtype}->{'reader'} = $types{$tokens[4]}->{'reader'}; + $types{$newtype}->{'writer'} = $types{$tokens[4]}->{'writer'}; + + } elsif ($tokens_nr == 6) { # must be ...USING... + + unless ($tokens[3] eq "USING") { + die "Invalid TYPE declaration in input file (line $line)\n"; + } + + $types{$newtype}->{'writer'} = $tokens[4]; + $types{$newtype}->{'reader'} = $tokens[5]; + $types{$newtype}->{'external'} = 'T'; + + } else { + die "Invalid TYPE declaration in input file (line $line)\n"; + } + + } elsif ($tokens[0] eq "RECORD") { + + $struct = $tokens[1]; + if ($types{$struct}) { + die "Attempt to re-define existing type $struct as a struct in input file (line $line)"; + } + $types{$struct}{'type'} = $type_record; + if ($tokens_nr < 3 or $tokens_nr > 6 or $tokens[$tokens_nr - 1] ne "{") { + die "Invalid record declaration in input file (line $line)"; + } + + my $extoffset = 2; + + if ($tokens_nr > 3) { + if ($tokens[2] ne "EXTENDS") { # Record declaration with explicit c type + $types{$struct}{'ctype'} = $tokens[2]; + $extoffset = 3; + } else { # Record name is the same as the c type name + $types{$struct}{'ctype'} = $struct; + } + } elsif ($tokens_nr == 3) { + $types{$struct}{'ctype'} = $struct; + } + + if (($tokens_nr > $extoffset + 1) && ($extoffset + 1 <= $tokens_nr)) { + if ($tokens[$extoffset] ne "EXTENDS") { + die "Invalid or improper keyword \"$tokens[$extoffset]\" in input file (line $line)"; + } + if ($extoffset + 2 >= $tokens_nr) { + die "RECORD \"$struct\" extends on unspecified type in input file (line $line)"; + } + my $ext_type = $tokens[$extoffset + 1]; + + if (!($types{$ext_type}{type} eq $type_record)) { + print "$types{$ext_type}{type}"; + die "RECORD \"$struct\" attempts to extend non-existing or non-record type \"$ext_type\" in input file (line $line)"; + } + + (@{$records{$struct}}) = (@{$records{$ext_type}}); # Copy type information from super type + } + + } else { + die "Invalid declaration \"$tokens[0]\" in line $line"; + } + } + } + + + } else { + + ($subtoken) = split ";"; # Get rid of trailing ;s + $tokens_nr = @tokens = split " ", $subtoken; + + if ($tokens_nr == 1 && $tokens[0] eq "%CFSML") { + + $parsing = 1; + + } elsif ($tokens[0] eq "%CFSMLWRITE" and $tokens[3] eq "INTO" and $tokens_nr >= 5) { + + insert_writer_code($type = $tokens[1], $datap = $tokens[2], $fh = $tokens[4]); + my $templine = $line + 1; + write_line_pp($templine, 1); # Yes, this sucks. + + } elsif (($tokens[0] eq "%CFSMLREAD") or ($tokens[0] eq "%CFSMLREAD-ATOMIC") and $tokens[3] eq "FROM" and $tokens_nr >= 5) { + + my $myeofvar = 0; + my $myfirsttoken = 0; + my $mylinecounter = 0; + + my $idcounter = 5; + + while ($idcounter < $tokens_nr) { + if ($tokens[$idcounter] eq "ERRVAR" and $tokens_nr >= $idcounter + 2) { + $myeofvar = $tokens[$idcounter + 1]; + $idcounter += 2; + } elsif ($tokens[$idcounter] eq "FIRSTTOKEN" and $tokens_nr >= $idcounter + 2) { + $myfirsttoken = $tokens[$idcounter + 1]; + $idcounter += 2; + } elsif ($tokens[$idcounter] eq "LINECOUNTER" and $tokens_nr >= $idcounter + 2) { + $mylinecounter = $tokens[$idcounter + 1]; + $idcounter += 2; + } else { + die "Unknown %CFSMLREAD operational token: $tokens[$idcounter]\n"; + } + } + insert_reader_code($type = $tokens[1], $datap = $tokens[2], + $fh = $tokens[4], $eofvar = $myeofvar, $firsttoken = $myfirsttoken, + $linecounter = $mylinecounter, $atomic = ($tokens[0] eq "%CFSMLREAD-ATOMIC")); + my $templine = $line + 1; + write_line_pp($templine, 1); # Yes, this sucks, too. + + } else { + print; + } + } + +} + +if ($parsing) { + print , "Warning: Missing %END CFSML\n"; +} diff --git a/engines/sci/engine/game.c b/engines/sci/engine/game.c new file mode 100644 index 0000000000..a6672326ef --- /dev/null +++ b/engines/sci/engine/game.c @@ -0,0 +1,787 @@ +/*************************************************************************** + game.c Copyright (C) 1999 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + + +/* Attempt to guess if recent version of Platform SDK */ +#ifdef _MSC_VER +# pragma message("******************** IMPORTANT MESSAGE ********************") +# pragma message("You must have installed a recent Platform and DirectX SDK") +# pragma message("for this build to be successful. Download MS SDKs from:") +# pragma message("www.microsoft.com/msdownload/platformsdk/sdkupdate") +# pragma message("***********************************************************") +# include +# if (WINVER < 0x500) +# error *** BUILD FAILED: Need more recent SDKs or fix your SDK paths in Tools Options *** +# endif +#endif + +#include +#include +#include +#include +#include "kernel_types.h" + +/* Structures and data from vm.c: */ +extern calls_struct_t *send_calls; +extern int send_calls_allocated; +extern int bp_flag; + + + +static int +_init_vocabulary(state_t *s) /* initialize vocabulary and related resources */ +{ + s->parser_lastmatch_word = SAID_NO_MATCH; + s->parser_rules = NULL; + + sciprintf("Initializing vocabulary\n"); + + if ((s->resmgr->sci_version < SCI_VERSION_01_VGA)&&(s->parser_words = vocab_get_words(s->resmgr, &(s->parser_words_nr)))) { + s->parser_suffices = vocab_get_suffices(s->resmgr, &(s->parser_suffices_nr)); + if ((s->parser_branches = vocab_get_branches(s->resmgr, &(s->parser_branches_nr)))) + /* Now build a GNF grammar out of this */ + s->parser_rules = vocab_build_gnf(s->parser_branches, s->parser_branches_nr); + + } else { + sciprintf("Assuming that this game does not use a parser.\n"); + s->parser_rules = NULL; + } + + + s->opcodes = vocabulary_get_opcodes(s->resmgr); + + if (!(s->selector_names = vocabulary_get_snames(s->resmgr, NULL, s->version))) { + sciprintf("_init_vocabulary(): Could not retreive selector names (vocab.997)!\n"); + return 1; + } + + for (s->selector_names_nr = 0; s->selector_names[s->selector_names_nr]; s->selector_names_nr++); + /* Counts the number of selector names */ + + script_map_selectors(s, &(s->selector_map)); + /* Maps a few special selectors for later use */ + + return 0; +} + +extern int _allocd_rules; +static void +_free_vocabulary(state_t *s) +{ + sciprintf("Freeing vocabulary\n"); + + if (s->parser_words) { + vocab_free_words(s->parser_words, s->parser_words_nr); + vocab_free_suffices(s->resmgr, s->parser_suffices, s->parser_suffices_nr); + vocab_free_branches(s->parser_branches); + vocab_free_rule_list(s->parser_rules); + } + + vocabulary_free_snames(s->selector_names); + vocabulary_free_knames(s->kernel_names); + vocabulary_free_opcodes(s->opcodes); + s->opcodes = NULL; + + s->selector_names = NULL; + s->kernel_names = NULL; + s->opcodes = NULL; +} + + +static int +_init_graphics_input(state_t *s) +{ + s->pic_priority_table = NULL; + s->pics = NULL; + s->pics_nr = 0; + return 0; +} + +static void +_sci1_alloc_system_colors(state_t *s) +{ + gfx_color_t white; + gfx_color_t black; + + white.visual.global_index = 255; + white.visual.r = white.visual.g = white.visual.b = 255; + white.alpha = 0; + white.priority = white.control = 0; + white.mask = GFX_MASK_VISUAL; + gfxop_set_system_color(s->gfx_state, &white); + + black.visual.global_index = 0; + black.visual.r = black.visual.g = black.visual.b = 0; + black.alpha = 0; + black.priority = black.control = 0; + black.mask = GFX_MASK_VISUAL; + gfxop_set_system_color(s->gfx_state, &black); +} + +int +_reset_graphics_input(state_t *s) +{ + resource_t *resource; + int font_nr; + gfx_color_t transparent; + sciprintf("Initializing graphics\n"); + + if (s->resmgr->sci_version <= SCI_VERSION_01) { + int i; + + for (i = 0; i < 16; i++) { + if (gfxop_set_color(s->gfx_state, &(s->ega_colors[i]), + gfx_sci0_image_colors[sci0_palette][i].r, + gfx_sci0_image_colors[sci0_palette][i].g, + gfx_sci0_image_colors[sci0_palette][i].b, + 0, -1, -1)) + return 1; + gfxop_set_system_color(s->gfx_state, &(s->ega_colors[i])); + } + } else + { + /* Check for Amiga palette file. */ + FILE *f = sci_fopen("spal", "rb"); + if (f) { + s->gfx_state->resstate->static_palette = + gfxr_read_pal1_amiga(&s->gfx_state->resstate->static_palette_entries, f); + fclose(f); + _sci1_alloc_system_colors(s); + } else { + resource = scir_find_resource(s->resmgr, sci_palette, 999, 1); + if (resource) { + if (s->version < SCI_VERSION(1,001,000)) + s->gfx_state->resstate->static_palette = + gfxr_read_pal1(999, &s->gfx_state->resstate->static_palette_entries, + resource->data, resource->size); + else + s->gfx_state->resstate->static_palette = + gfxr_read_pal11(999, &s->gfx_state->resstate->static_palette_entries, + resource->data, resource->size); + _sci1_alloc_system_colors(s); + scir_unlock_resource(s->resmgr, resource, sci_palette, 999); + } else + sciprintf("Couldn't find the default palette!\n"); + } + } + transparent.mask = 0; + + gfxop_fill_box(s->gfx_state, gfx_rect(0, 0, 320, 200), s->ega_colors[0]); /* Fill screen black */ + gfxop_update(s->gfx_state); + + s->mouse_pointer_view = s->mouse_pointer_loop = s->mouse_pointer_cel = -1; /* No mouse pointer resource */ + s->save_mouse_pointer_view = s->save_mouse_pointer_loop = s->save_mouse_pointer_cel = -1; /* No mouse pointer resource */ + gfxop_set_pointer_position(s->gfx_state, gfx_point(160, 150)); + + s->mouse_pointer_view = s->mouse_pointer_loop = s->mouse_pointer_cel = -1; /* No mouse pointer resource */ + s->save_mouse_pointer_view = s->save_mouse_pointer_loop = s->save_mouse_pointer_cel = -1; /* No mouse pointer resource */ + + + s->pic_is_new = 0; + s->pic_visible_map = GFX_MASK_NONE; /* Other values only make sense for debugging */ + s->dyn_views = NULL; /* no DynViews */ + s->drop_views = NULL; /* And, consequently, no list for dropped views */ + + s->priority_first = 42; /* Priority zone 0 ends here */ + + if (s->version < SCI_VERSION_FTU_PRIORITY_14_ZONES) + s->priority_last = 200; else + s->priority_last = 190; + + font_nr = -1; + do { + resource = scir_test_resource(s->resmgr, sci_font, ++font_nr); + } while ((!resource) && (font_nr < sci_max_resource_nr[s->resmgr->sci_version])); + + if (!resource) { + sciprintf("No text font was found.\n"); + return 1; + } + + s->visual = gfxw_new_visual(s->gfx_state, font_nr); + + s->wm_port = gfxw_new_port(s->visual, NULL, s->gfx_state->options->pic_port_bounds, s->ega_colors[0], transparent); + s->iconbar_port = gfxw_new_port(s->visual, NULL, gfx_rect(0, 0, 320, 200), s->ega_colors[0], transparent); + s->iconbar_port->flags |= GFXW_FLAG_NO_IMPLICIT_SWITCH; + + if (s->resmgr->sci_version >= SCI_VERSION_01_VGA) + { + gfx_color_t fgcolor; + gfx_color_t bgcolor; + +#if 0 + fgcolor.visual = s->gfx_state->resstate->static_palette[0]; + fgcolor.mask = GFX_MASK_VISUAL; + bgcolor.visual = s->gfx_state->resstate->static_palette[255]; + bgcolor.mask = GFX_MASK_VISUAL; +#endif + s->titlebar_port = gfxw_new_port(s->visual, NULL, gfx_rect(0, 0, 320, 10), + fgcolor, bgcolor); + } else + s->titlebar_port = gfxw_new_port(s->visual, NULL, gfx_rect(0, 0, 320, 10), + s->ega_colors[0], s->ega_colors[15]); + s->titlebar_port->color.mask |= GFX_MASK_PRIORITY; + s->titlebar_port->color.priority = 11; + s->titlebar_port->bgcolor.mask |= GFX_MASK_PRIORITY; + s->titlebar_port->bgcolor.priority = 11; + s->titlebar_port->flags |= GFXW_FLAG_NO_IMPLICIT_SWITCH; + + /* but this is correct */ + s->picture_port = gfxw_new_port(s->visual, NULL, s->gfx_state->options->pic_port_bounds, s->ega_colors[0], transparent); + + s->pics_drawn_nr = 0; + + s->visual->add(GFXWC(s->visual), GFXW(s->wm_port)); + s->visual->add(GFXWC(s->visual), GFXW(s->titlebar_port)); + s->visual->add(GFXWC(s->visual), GFXW(s->picture_port)); + s->visual->add(GFXWC(s->visual), GFXW(s->iconbar_port)); + /* Add ports to visual */ + + s->port = s->picture_port; /* Currently using the picture port */ + +#if 0 + s->titlebar_port->bgcolor.mask |= GFX_MASK_PRIORITY; + s->titlebar_port->bgcolor.priority = 11; /* Standard priority for the titlebar port */ +#endif + + return 0; +} + +int +game_init_graphics(state_t *s) +{ +#ifndef WITH_PIC_SCALING + if (s->gfx_state->options->pic0_unscaled == 0) + sciprintf("WARNING: Pic scaling was disabled; your version of FreeSCI has no support for scaled pic drawing built in.\n"); + + s->gfx_state->options->pic0_unscaled = 1; +#endif + return _reset_graphics_input(s); +} + + +static void +_free_graphics_input(state_t *s) +{ + sciprintf("Freeing graphics\n"); + + s->visual->widfree(GFXW(s->visual)); + + s->wm_port = s->titlebar_port = s->picture_port = NULL; + s->visual = NULL; + s->dyn_views = NULL; + s->port = NULL; + + if (s->pics) + sci_free(s->pics); + s->pics = NULL; +} + +/*------------------------------------------------------------*/ + +int +game_init_sound(state_t *s, int sound_flags) +{ + if (s->resmgr->sci_version >= SCI_VERSION_01) + sound_flags |= SFX_STATE_FLAG_MULTIPLAY; + + s->sfx_init_flags = sound_flags; + sfx_init(&s->sound, s->resmgr, sound_flags); + return 0; +} + + +/* Maps a class ID to the script the corresponding class is contained in */ +/* Returns the script number suggested by vocab.996, or -1 if there's none */ +static int +suggested_script(resource_t *res, unsigned int class) +{ + int offset; + if (!res || class >= res->size >> 2) + return -1; + + offset = 2 + (class << 2); + + return getInt16(res->data + offset); +} + + +int +test_cursor_style(state_t *s) +{ + int resource_nr = 0; + int ok = 0; + + do { + ok |= scir_test_resource(s->resmgr, sci_cursor, resource_nr++) != NULL; + } while (resource_nr < 1000 && !ok); + + return ok; +} + +int +create_class_table_sci11(state_t *s) +{ + int scriptnr; + unsigned int seeker_offset; + char *seeker_ptr; + int classnr; + + resource_t *vocab996 = scir_find_resource(s->resmgr, sci_vocab, 996, 1); + + if (!vocab996) + s->classtable_size = 20; + else + s->classtable_size = vocab996->size >> 2; + + s->classtable = (class_t*)sci_calloc(sizeof(class_t), s->classtable_size); + + for (scriptnr = 0; scriptnr < 1000; scriptnr++) { + resource_t *heap = scir_find_resource(s->resmgr, sci_heap, + scriptnr, 0); + + if (heap) { + int global_vars = getUInt16(heap->data + 2); + + seeker_ptr = (char*)heap->data + 4 + global_vars*2; + seeker_offset = 4 + global_vars*2; + + while (getUInt16((byte*)seeker_ptr) == SCRIPT_OBJECT_MAGIC_NUMBER) + { + if (getUInt16((byte*)seeker_ptr + 14) & SCRIPT_INFO_CLASS) + { + classnr = getUInt16((byte*)seeker_ptr + 10); + if (classnr >= s->classtable_size) { + + if (classnr >= SCRIPT_MAX_CLASSTABLE_SIZE) { + fprintf(stderr,"Invalid class number 0x%x in script.%d(0x%x), offset %04x\n", + classnr, scriptnr, scriptnr, seeker_offset); + return 1; + } + + s->classtable = (class_t*)sci_realloc(s->classtable, sizeof(class_t) * (classnr + 1)); + memset(&(s->classtable[s->classtable_size]), 0, + sizeof(class_t) * (1 + classnr - s->classtable_size)); /* Clear after resize */ + + s->classtable_size = classnr + 1; /* Adjust maximum number of entries */ + } + + s->classtable[classnr].reg.offset = seeker_offset; + s->classtable[classnr].reg.segment = 0; + s->classtable[classnr].script = scriptnr; + } + + seeker_ptr += getUInt16((byte*)seeker_ptr + 2) * 2; + seeker_offset += getUInt16((byte*)seeker_ptr + 2) * 2; + } + } + } + + return 0; +} +static int +create_class_table_sci0(state_t *s) +{ + int scriptnr; + unsigned int seeker; + int classnr; + int magic_offset; /* For strange scripts in older SCI versions */ + + resource_t *vocab996 = scir_find_resource(s->resmgr, sci_vocab, 996, 1); + + if (!vocab996) + s->classtable_size = 20; + else + s->classtable_size = vocab996->size >> 2; + + s->classtable = (class_t*)sci_calloc(sizeof(class_t), s->classtable_size); + + for (scriptnr = 0; scriptnr < 1000; scriptnr++) { + int objtype = 0; + resource_t *script = scir_find_resource(s->resmgr, sci_script, + scriptnr, 0); + + if (script) { + if (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER) + magic_offset = seeker = 2; + else + magic_offset = seeker = 0; + + do { + + while (seeker < script->size) { + unsigned int lastseeker = seeker; + objtype = getInt16(script->data + seeker); + if (objtype == sci_obj_class || objtype == sci_obj_terminator) + break; + seeker += getInt16(script->data + seeker + 2); + if (seeker <= lastseeker) { + sciprintf("Warning: Script version is invalid.\n"); + sci_free(s->classtable); + return SCI_ERROR_INVALID_SCRIPT_VERSION; + } + } + + if (objtype == sci_obj_class) { + int sugg_script; + + seeker -= SCRIPT_OBJECT_MAGIC_OFFSET; /* Adjust position; script home is base +8 bytes */ + + classnr = getInt16(script->data + seeker + 4 + SCRIPT_SPECIES_OFFSET); + if (classnr >= s->classtable_size) { + + if (classnr >= SCRIPT_MAX_CLASSTABLE_SIZE) { + fprintf(stderr,"Invalid class number 0x%x in script.%d(0x%x), offset %04x\n", + classnr, scriptnr, scriptnr, seeker); + return 1; + } + + s->classtable = (class_t*)sci_realloc(s->classtable, sizeof(class_t) * (classnr + 1)); + memset(&(s->classtable[s->classtable_size]), 0, + sizeof(class_t) * (1 + classnr - s->classtable_size)); /* Clear after resize */ + + s->classtable_size = classnr + 1; /* Adjust maximum number of entries */ + } + + sugg_script = suggested_script(vocab996, classnr); + + /* First, test whether the script hasn't been claimed, or if it's been claimed by the wrong script */ + + if (sugg_script == -1 || scriptnr == sugg_script /*|| !s->classtable[classnr].reg.segment*/) { + /* Now set the home script of the class */ + s->classtable[classnr].reg.offset = seeker + 4 - magic_offset; + s->classtable[classnr].reg.segment = 0; + s->classtable[classnr].script = scriptnr; + } + + seeker += SCRIPT_OBJECT_MAGIC_OFFSET; /* Re-adjust position */ + + seeker += getInt16(script->data + seeker + 2); /* Move to next */ + } + + } while (objtype != sci_obj_terminator && seeker <= script->size); + + } + } + scir_unlock_resource(s->resmgr, vocab996, sci_vocab, 996); + vocab996 = NULL; + return 0; +} + +/* Architectural stuff: Init/Unintialize engine */ +int +script_init_engine(state_t *s, sci_version_t version) +{ + int result; + + s->max_version = SCI_VERSION(9,999,999); /* :-) */ + s->min_version = 0; /* Set no real limits */ + s->version = SCI_VERSION_DEFAULT_SCI0; + s->kernel_opt_flags = 0; + + if (!version) { + s->version_lock_flag = 0; + } else { + s->version = version; + s->version_lock_flag = 1; /* Lock version */ + } + + script_detect_versions(s); + + if (s->version >= SCI_VERSION(1,001,000)) + result = create_class_table_sci11(s); + else + result = create_class_table_sci0(s); + + sm_init(&s->seg_manager, s->version >= SCI_VERSION(1,001,000)); + s->gc_countdown = GC_INTERVAL - 1; + + if (result) + { + sciprintf("Failed to initialize class table\n"); + return 1; + } + + s->script_000_segment = script_get_segment(s, 0, SCRIPT_GET_LOCK); + + if (s->script_000_segment <= 0) { + sciprintf("Failed to instantiate script.000\n"); + return 1; + } + + s->script_000 = &(s->seg_manager.heap[s->script_000_segment]->data.script); + + + s->sys_strings = sm_allocate_sys_strings(&s->seg_manager, + &s->sys_strings_segment); + /* Allocate static buffer for savegame and CWD directories */ + sys_string_acquire(s->sys_strings, SYS_STRING_SAVEDIR, "savedir", MAX_SAVE_DIR_SIZE); + + s->save_dir_copy = make_reg(s->sys_strings_segment, SYS_STRING_SAVEDIR); + s->save_dir_edit_offset = 0; + + s->r_acc = s->r_prev = NULL_REG; + s->r_amp_rest = 0; + + s->execution_stack = NULL; /* Start without any execution stack */ + s->execution_stack_base = -1; /* No vm is running yet */ + s->execution_stack_pos = -1; /* Start at execution stack position 0 */ + + + s->kernel_names = vocabulary_get_knames(s->resmgr, &s->kernel_names_nr); + script_map_kernel(s); + /* Maps the kernel functions */ + + if (_init_vocabulary(s)) return 1; + if (s->selector_map.cantBeHere != -1) + version_require_later_than(s, SCI_VERSION_FTU_INVERSE_CANBEHERE); + + s->restarting_flags = SCI_GAME_IS_NOT_RESTARTING; + + s->bp_list = NULL; /* No breakpoints defined */ + s->have_bp = 0; + + s->file_handles_nr = 5; + s->file_handles = (FILE**)sci_calloc(sizeof(FILE *), s->file_handles_nr); + /* Allocate memory for file handles */ + + sci_init_dir(&(s->dirseeker)); + s->dirseeker_outbuffer = NULL_REG; + /* Those two are used by FileIO for FIND_FIRST, FIND_NEXT */ + + if (s->version >= SCI_VERSION_FTU_LOFS_ABSOLUTE && + s->version < SCI_VERSION(1,001,000)) + sm_set_export_width(&s->seg_manager, 1); + else + sm_set_export_width(&s->seg_manager, 0); + + sciprintf("Engine initialized\n"); + + if (_init_graphics_input(s)) + return 1; + + return 0; +} + + +void +script_set_gamestate_save_dir(state_t *s, const char* path) +{ + sys_string_set(s->sys_strings, SYS_STRING_SAVEDIR, path); +} + +void +script_free_vm_memory(state_t *s) +{ + int i; + + sciprintf("Freeing VM memory\n"); + s->save_dir_copy_buf = NULL; + + sci_free(s->classtable); + s->classtable = NULL; + + /* Close all opened file handles */ +#ifndef _DOS + for (i = 1; i < s->file_handles_nr; i++) + if (s->file_handles[i]) + fclose(s->file_handles[i]); +#endif + + sci_free(s->file_handles); + s->file_handles = NULL; + + /* FIXME: file handles will NOT be closed under DOS. DJGPP generates an + exception fault whenever you try to close a never-opened file */ +} + +extern void +free_kfunct_tables(state_t *s); +/* From kernel.c */ + +void +script_free_engine(state_t *s) +{ + script_free_vm_memory(s); + + sciprintf("Freeing state-dependant data\n"); + + free_kfunct_tables(s); + + _free_vocabulary(s); + +} + +void +script_free_breakpoints(state_t *s) +{ + breakpoint_t *bp, *bp_next; + + /* Free breakpoint list */ + bp = s->bp_list; + while (bp) { + bp_next = bp->next; + if (bp->type == BREAK_SELECTOR) sci_free (bp->data.name); + free (bp); + bp = bp_next; + } + + s->bp_list = NULL; +} + +/*************************************************************/ +/* Game instance stuff: Init/Unitialize state-dependant data */ +/*************************************************************/ + + +int +game_init(state_t *s) +{ +#ifdef __GNUC__ +# warning "Fixme: Use new VM instantiation code all over the place" +#endif + reg_t game_obj; /* Address of the game object */ + dstack_t *stack; + + stack = sm_allocate_stack(&s->seg_manager, VM_STACK_SIZE, + &s->stack_segment); + s->stack_base = stack->entries; + s->stack_top = s->stack_base + VM_STACK_SIZE; + + if (!script_instantiate(s, 0)) { + sciprintf("game_init(): Could not instantiate script 0\n"); + return 1; + } + + s->parser_valid = 0; /* Invalidate parser */ + s->parser_event = NULL_REG; /* Invalidate parser event */ + + s->synonyms = NULL; + s->synonyms_nr = 0; /* No synonyms */ + + /* Initialize send_calls buffer */ + + if (!send_calls_allocated) + send_calls = (calls_struct_t*)sci_calloc(sizeof(calls_struct_t), send_calls_allocated = 16); + + if (s->gfx_state && _reset_graphics_input(s)) + return 1; + + s->successor = NULL; /* No successor */ + s->status_bar_text = NULL; /* Status bar is blank */ + s->status_bar_foreground = 0; + s->status_bar_background = s->resmgr->sci_version >= SCI_VERSION_01_VGA ? 255 : 15; + + sys_string_acquire(s->sys_strings, SYS_STRING_PARSER_BASE, "parser-base", MAX_PARSER_BASE); + s->parser_base = make_reg(s->sys_strings_segment, SYS_STRING_PARSER_BASE); + + sci_get_current_time(&(s->game_start_time)); /* Get start time */ + memcpy(&(s->last_wait_time), &(s->game_start_time), sizeof(GTimeVal)); + /* Use start time as last_wait_time */ + + s->debug_mode = 0x0; /* Disable all debugging */ + s->onscreen_console = 0; /* No onscreen console unless explicitly requested */ + + srand(time(NULL)); /* Initialize random number generator */ + + /* script_dissect(0, s->selector_names, s->selector_names_nr); */ + game_obj = script_lookup_export(s, 0, 0); + /* The first entry in the export table of script 0 points to the game object */ + + s->game_name = sci_strdup(obj_get_name(s, game_obj)); + + if (!s->game_name) { + sciprintf("Error: script.000, export 0 ("PREG") does not\n" + " yield an object with a name -> sanity check failed\n", + PRINT_REG(game_obj)); + return 1; + } + + sciprintf(" \"%s\" at "PREG"\n", s->game_name, PRINT_REG(game_obj)); + + if (strlen((char *) s->game_name) >= MAX_GAMEDIR_SIZE) { + + s->game_name[MAX_GAMEDIR_SIZE - 1] = 0; /* Fix length with brute force */ + sciprintf(" Designation too long; was truncated to \"%s\"\n", s->game_name); + } + + s->game_obj = game_obj; + + /* Mark parse tree as unused */ + s->parser_nodes[0].type = PARSE_TREE_NODE_LEAF; + s->parser_nodes[0].content.value = 0; + + s->menubar = menubar_new(); /* Create menu bar */ + + return 0; +} + +int +game_exit(state_t *s) +{ + if (s->execution_stack) { + sci_free(s->execution_stack); + } + +#if 0 + sfx_exit(&s->sound); +/* Reinit because some other code depends on having a valid state */ + game_init_sound(s, SFX_STATE_FLAG_NOSOUND); +#else +#endif + + sm_destroy(&s->seg_manager); + + if (s->synonyms_nr) { + sci_free(s->synonyms); + s->synonyms = NULL; + s->synonyms_nr = 0; + } + + sciprintf("Freeing miscellaneous data...\n"); + +#ifdef __GNUC__ +#warning "Free parser segment here" +#endif + if (send_calls_allocated) { + sci_free(send_calls); + send_calls_allocated = 0; + } + +#ifdef __GNUC__ +#warning "Free scripts here" +#endif + + menubar_free(s->menubar); + + _free_graphics_input(s); + + sci_free(s->game_name); + + return 0; +} + diff --git a/engines/sci/engine/gc.c b/engines/sci/engine/gc.c new file mode 100644 index 0000000000..063c58a076 --- /dev/null +++ b/engines/sci/engine/gc.c @@ -0,0 +1,334 @@ +/*************************************************************************** + Copyright (C) 2005 Christoph Reichenbach + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +***************************************************************************/ + +#include "gc.h" + +#define WORKLIST_CHUNK_SIZE 32 + +/*#define DEBUG_GC*/ +/*#define DEBUG_GC_VERBOSE*/ + +typedef struct _worklist { + int used; + reg_t entries[WORKLIST_CHUNK_SIZE]; + struct _worklist *next; +} worklist_t; + +static worklist_t * +fresh_worklist(worklist_t *old) +{ + worklist_t *retval = (worklist_t*)sci_malloc(sizeof(worklist_t)); + retval->used = 0; + retval->next = old; + return retval; +} + +static worklist_t * +new_worklist() +{ + return fresh_worklist(NULL); +} + +static void +worklist_push(worklist_t **wlp, reg_t_hash_map_ptr hashmap, reg_t reg) +{ + worklist_t *wl = *wlp; + char added; + + if (!reg.segment) /* No numbers */ + return; + +#ifdef DEBUG_GC_VERBOSE + sciprintf("[GC] Adding "PREG"\n", PRINT_REG(reg)); +#endif + + reg_t_hash_map_check_value(hashmap, reg, 1, &added); + + if (!added) + return; /* already dealt with it */ + + if (!wl || wl->used == WORKLIST_CHUNK_SIZE) + *wlp = wl = fresh_worklist(wl); + + wl->entries[wl->used++] = reg; +} + +static int +worklist_has_next(worklist_t *wl) +{ + return (wl && wl->used); +} + +static reg_t +worklist_pop(worklist_t **wlp) +{ + worklist_t *wl = *wlp; + reg_t retval; + + if (!wl || !wl->used) { + fprintf(stderr, "Attempt to pop from empty worklist"); + exit(1); + } + + retval = wl->entries[--wl->used]; + + if (!wl->used) { + *wlp = wl->next; + sci_free(wl); + } + + return retval; +} + +static void +free_worklist(worklist_t *wl) +{ + if (wl) { + if (wl->next) + free_worklist(wl->next); + sci_free(wl); + } +} + +typedef struct { + seg_interface_t **interfaces; + int interfaces_nr; + reg_t_hash_map_ptr normal_map; +} normaliser_t; + +void +store_normalised(void *pre_normaliser, reg_t reg, int _) +{ + seg_interface_t *interfce; + normaliser_t *normaliser = (normaliser_t *) pre_normaliser; + interfce = (reg.segment < normaliser->interfaces_nr) + ? normaliser->interfaces[reg.segment] + : NULL; + + if (interfce) { + reg = interfce->find_canonic_address(interfce, reg); + reg_t_hash_map_check_value(normaliser->normal_map, reg, 1, NULL); + } +} + +static reg_t_hash_map_ptr +normalise_hashmap_ptrs(reg_t_hash_map_ptr nonnormal_map, seg_interface_t **interfaces, int interfaces_nr) +{ + normaliser_t normaliser; + + normaliser.normal_map = new_reg_t_hash_map(); + normaliser.interfaces_nr = interfaces_nr; + normaliser.interfaces = interfaces; + apply_to_reg_t_hash_map(nonnormal_map, &normaliser, &store_normalised); + + return normaliser.normal_map; +} + + +typedef struct { + reg_t_hash_map_ptr nonnormal_map; + worklist_t **worklist_ref; +} worklist_manager_t; + +void +add_outgoing_refs(void *pre_wm, reg_t addr) +{ + worklist_manager_t *wm = (worklist_manager_t *) pre_wm; + worklist_push(wm->worklist_ref, wm->nonnormal_map, addr); +} + +reg_t_hash_map_ptr +find_all_used_references(state_t *s) +{ + seg_manager_t *sm = &(s->seg_manager); + seg_interface_t **interfaces = (seg_interface_t**)sci_calloc(sizeof(seg_interface_t *), sm->heap_size); + reg_t_hash_map_ptr nonnormal_map = new_reg_t_hash_map(); + reg_t_hash_map_ptr normal_map = NULL; + worklist_t *worklist = new_worklist(); + worklist_manager_t worklist_manager; + int i; + + worklist_manager.worklist_ref = &worklist; + worklist_manager.nonnormal_map = nonnormal_map; + + for (i = 1; i < sm->heap_size; i++) + if (sm->heap[i] == NULL) + interfaces[i] = NULL; + else + interfaces[i] = get_seg_interface(sm, i); + + /* Initialise */ + /* Init: Registers */ + worklist_push(&worklist, nonnormal_map, s->r_acc); + worklist_push(&worklist, nonnormal_map, s->r_prev); + /* Init: Value Stack */ + /* We do this one by hand since the stack doesn't know the current execution stack */ + { + exec_stack_t *xs = s->execution_stack + s->execution_stack_pos; + reg_t *pos; + + for (pos = s->stack_base; pos < xs->sp; pos++) + worklist_push(&worklist, nonnormal_map, *pos); + } +#ifdef DEBUG_GC_VERBOSE + sciprintf("[GC] -- Finished adding value stack"); +#endif + + + /* Init: Execution Stack */ + for (i = 0; i <= s->execution_stack_pos; i++) { + exec_stack_t *es = s->execution_stack + i; + + if (es->type != EXEC_STACK_TYPE_KERNEL) { + worklist_push(&worklist, nonnormal_map, es->objp); + worklist_push(&worklist, nonnormal_map, es->sendp); + if (es->type == EXEC_STACK_TYPE_VARSELECTOR) + worklist_push(&worklist, nonnormal_map, *(es->addr.varp)); + } + } +#ifdef DEBUG_GC_VERBOSE + sciprintf("[GC] -- Finished adding execution stack"); +#endif + + /* Init: Explicitly loaded scripts */ + for (i = 1; i < sm->heap_size; i++) + if (interfaces[i] + && interfaces[i]->type_id == MEM_OBJ_SCRIPT) { + script_t *script = &(interfaces[i]->mobj->data.script); + + if (script->lockers) { /* Explicitly loaded? */ + int obj_nr; + + /* Locals, if present */ + worklist_push(&worklist, nonnormal_map, make_reg(script->locals_segment, 0)); + + /* All objects (may be classes, may be indirectly reachable) */ + for (obj_nr = 0; obj_nr < script->objects_nr; obj_nr++) { + object_t *obj = script->objects + obj_nr; + worklist_push(&worklist, + nonnormal_map, + obj->pos); + } + } + } +#ifdef DEBUG_GC_VERBOSE + sciprintf("[GC] -- Finished explicitly loaded scripts, done with root set"); +#endif + + + /* Run Worklist Algorithm */ + while (worklist_has_next(worklist)) { + reg_t reg = worklist_pop(&worklist); + if (reg.segment != s->stack_segment) { /* No need to repeat this one */ +#ifdef DEBUG_GC_VERBOSE + sciprintf("[GC] Checking "PREG"\n", PRINT_REG(reg)); +#endif + if (reg.segment < sm->heap_size + && interfaces[reg.segment]) + interfaces[reg.segment]->list_all_outgoing_references(interfaces[reg.segment], + s, + reg, + &worklist_manager, + add_outgoing_refs); + } + } + + /* Normalise */ + normal_map = normalise_hashmap_ptrs(nonnormal_map, interfaces, sm->heap_size); + + /* Cleanup */ + for (i = 1; i < sm->heap_size; i++) + if (interfaces[i]) + interfaces[i]->deallocate_self(interfaces[i]); + sci_free(interfaces); + free_reg_t_hash_map(nonnormal_map); + return normal_map; +} + + +typedef struct { + seg_interface_t *interfce; +#ifdef DEBUG_GC + char *segnames[MEM_OBJ_MAX + 1]; + int segcount[MEM_OBJ_MAX + 1]; +#endif + reg_t_hash_map_ptr use_map; +} deallocator_t; + +void +free_unless_used (void *pre_use_map, reg_t addr) +{ + deallocator_t *deallocator = (deallocator_t *) pre_use_map; + reg_t_hash_map_ptr use_map = deallocator->use_map; + + if (0 > reg_t_hash_map_check_value(use_map, addr, 0, NULL)) { + /* Not found -> we can free it */ + deallocator->interfce->free_at_address(deallocator->interfce, addr); +#ifdef DEBUG_GC + sciprintf("[GC] Deallocating "PREG"\n", PRINT_REG(addr)); + deallocator->segcount[deallocator->interfce->type_id]++; +#endif + } + +} + +void +run_gc(state_t *s) +{ + int seg_nr; + deallocator_t deallocator; + seg_manager_t *sm = &(s->seg_manager); + +#ifdef DEBUG_GC + c_segtable(s); + sciprintf("[GC] Running...\n"); + memset(&(deallocator.segcount), 0, sizeof(int) * (MEM_OBJ_MAX + 1)); +#endif + + deallocator.use_map = find_all_used_references(s); + + for (seg_nr = 1; seg_nr < sm->heap_size; seg_nr++) + if (sm->heap[seg_nr] != NULL) { + deallocator.interfce = get_seg_interface(sm, seg_nr); +#ifdef DEBUG_GC + deallocator.segnames[deallocator.interfce->type_id] = deallocator.interfce->type; +#endif + + deallocator.interfce->list_all_deallocatable(deallocator.interfce, + &deallocator, + free_unless_used); + + deallocator.interfce->deallocate_self(deallocator.interfce); + } + + free_reg_t_hash_map(deallocator.use_map); + +#ifdef DEBUG_GC + { + int i; + sciprintf("[GC] Summary:\n"); + for (i = 0; i <= MEM_OBJ_MAX; i++) + if (deallocator.segcount[i]) + sciprintf("\t%d\t* %s\n", + deallocator.segcount[i], + deallocator.segnames[i]); + } +#endif +} diff --git a/engines/sci/engine/gc.h b/engines/sci/engine/gc.h new file mode 100644 index 0000000000..b0368ee8c2 --- /dev/null +++ b/engines/sci/engine/gc.h @@ -0,0 +1,41 @@ +/*************************************************************************** + Copyright (C) 2005 Christoph Reichenbach + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +***************************************************************************/ + + +#ifndef GC_H_ +#define GC_H_ + +#include +#include + +reg_t_hash_map_ptr +find_all_used_references(state_t *s); +/* Finds all used references and normalises them to their memory addresses +** Parameters: (state_t *) s: The state to gather all information from +** Returns : (reg_t_hash_map_ptr) A hash map containing entries for all used references +*/ + +void +run_gc(state_t *s); +/* Runs garbage collection on the current system state +** Parameters: (state_t *) s: The state in which we should gc +*/ + +#endif /* !defined(GC_H_) */ diff --git a/engines/sci/engine/grammar.c b/engines/sci/engine/grammar.c new file mode 100644 index 0000000000..557c535b77 --- /dev/null +++ b/engines/sci/engine/grammar.c @@ -0,0 +1,720 @@ +/************************************************************************** + grammar.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +/* Functionality to transform the context-free SCI grammar rules into +** strict Greibach normal form (strict GNF), and to test SCI input against +** that grammar, writing an appropriate node tree if successful. +*/ + +#include +#include +#include +#include +#include + +#define TOKEN_OPAREN 0xff000000 +#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_TERMINAL (TOKEN_TERMINAL_CLASS | TOKEN_TERMINAL_GROUP) + + int _allocd_rules = 0; + +static void +vocab_print_rule(parse_rule_t *rule) +{ + int i; + int wspace = 0; + + if (!rule) { + sciprintf("NULL rule"); + return; + } + + sciprintf("[%03x] -> ", rule->id); + + if (!rule->length) + sciprintf("e"); + + for(i = 0; i < rule->length; i++) { + int token = rule->data[i]; + + if (token == TOKEN_OPAREN) { + + if (i == rule->first_special) + sciprintf("_"); + + sciprintf("("); + wspace = 0; + } else if (token == TOKEN_CPAREN) { + + if (i == rule->first_special) + sciprintf("_"); + + sciprintf(")"); + wspace = 0; + } else { + if (wspace) + sciprintf(" "); + + if (i == rule->first_special) + sciprintf("_"); + if (token & TOKEN_TERMINAL_CLASS) + sciprintf("C(%04x)", token & 0xffff); + else if (token & TOKEN_TERMINAL_GROUP) + sciprintf("G(%04x)", token & 0xffff); + else if (token & TOKEN_STUFFING_WORD) + sciprintf("%03x", token & 0xffff); + else + sciprintf("[%03x]", token); /* non-terminal */ + wspace = 1; + } + + if (i == rule->first_special) + sciprintf("_"); + } + sciprintf(" [%d specials]", rule->specials_nr); +} + + +static void +_vfree(parse_rule_t *rule) +{ + free(rule); + --_allocd_rules; + rule = NULL; +} + +static parse_rule_t * +_vbuild(int id, int argc, ...) +{ + va_list args; + int i; + parse_rule_t *rule = (parse_rule_t*)sci_malloc(sizeof(int) * (argc + 4)); + + ++_allocd_rules; + rule->id = id; + rule->first_special = 0; + rule->specials_nr = 0; + rule->length = argc; + va_start(args, argc); + for (i = 0; i < argc; i++) { + int v; + rule->data[i] = v = va_arg(args, int); + if ((v & TOKEN_TERMINAL) + || !(v & TOKEN_NON_NT)) { + + ++rule->specials_nr; + + if (!rule->first_special) + rule->first_special = i; + } + } + va_end(args); + return rule; +} + +static parse_rule_t * +_vcat(int id, parse_rule_t *a, parse_rule_t *b) +{ + parse_rule_t *rule = (parse_rule_t*)sci_malloc(sizeof(int) * (a->length + b->length + 4)); + + rule->id = id; + rule->length = a->length + b->length; + rule->specials_nr = a->specials_nr + b->specials_nr; + rule->first_special = a->first_special; + ++_allocd_rules; + + memcpy(rule->data, a->data, sizeof(int) * a->length); + memcpy(&(rule->data[a->length]), b->data, sizeof(int) * b->length); + + return rule; +} + +static parse_rule_t * +_vdup(parse_rule_t *a) +{ + parse_rule_t *rule = (parse_rule_t*)sci_malloc(sizeof(int) * (a->length + 4)); + + rule->id = a->id; + rule->length = a->length; + rule->specials_nr = a->specials_nr; + rule->first_special = a->first_special; + ++_allocd_rules; + + memcpy(rule->data, a->data, sizeof(int) * a->length); + + return rule; +} + +static parse_rule_t * +_vinsert(parse_rule_t *turkey, parse_rule_t *stuffing) +{ + int firstnt = turkey->first_special; + parse_rule_t *rule; + + while ((firstnt < turkey->length) + && (turkey->data[firstnt] & TOKEN_NON_NT)) + firstnt++; + + if ((firstnt == turkey->length) + || (turkey->data[firstnt] != stuffing->id)) + return NULL; + + rule = (parse_rule_t*)sci_malloc(sizeof(int) * (turkey->length - 1 + stuffing->length + 4)); + rule->id = turkey->id; + rule->specials_nr = turkey->specials_nr + stuffing->specials_nr - 1; + rule->first_special = firstnt + stuffing->first_special; + rule->length = turkey->length - 1 + stuffing->length; + ++_allocd_rules; + + if (firstnt > 0) + memcpy(rule->data, turkey->data, sizeof(int) * firstnt); + memcpy(&(rule->data[firstnt]), stuffing->data, sizeof(int) * stuffing->length); + if (firstnt < turkey->length - 1) + memcpy(&(rule->data[firstnt + stuffing->length]), &(turkey->data[firstnt + 1]), + sizeof(int) * (turkey->length - firstnt - 1)); + + return rule; +} + + +static int +_greibach_rule_p(parse_rule_t *rule) +{ + int pos = rule->first_special; + while (pos < rule->length + && (rule->data[pos] & TOKEN_NON_NT) + && !(rule->data[pos] & TOKEN_TERMINAL)) + ++pos; + + if (pos == rule->length) + return 0; + + return (rule->data[pos] & TOKEN_TERMINAL); +} + +static parse_rule_t * +_vbuild_rule(parse_tree_branch_t *branch) +{ + parse_rule_t *rule; + int tokens = 0, tokenpos = 0, i; + + while (tokenpos < 10 && branch->data[tokenpos]) { + int type = branch->data[tokenpos]; + tokenpos += 2; + + if ((type == VOCAB_TREE_NODE_COMPARE_TYPE) + || (type == VOCAB_TREE_NODE_COMPARE_GROUP) + || (type == VOCAB_TREE_NODE_FORCE_STORAGE)) + ++tokens; + else if (type > VOCAB_TREE_NODE_LAST_WORD_STORAGE) + tokens += 5; + else return NULL; /* invalid */ + } + + rule = (parse_rule_t*)sci_malloc(sizeof(int) * (4 + tokens)); + + ++_allocd_rules; + rule->id = branch->id; + rule->specials_nr = tokenpos >> 1; + rule->length = tokens; + rule->first_special = 0; + + tokens = 0; + for (i = 0; i < tokenpos; i += 2) { + int type = branch->data[i]; + int value = branch->data[i + 1]; + + if (type == VOCAB_TREE_NODE_COMPARE_TYPE) + rule->data[tokens++] = value | TOKEN_TERMINAL_CLASS; + else if (type == VOCAB_TREE_NODE_COMPARE_GROUP) + rule->data[tokens++] = value | TOKEN_TERMINAL_GROUP; + else if (type == VOCAB_TREE_NODE_FORCE_STORAGE) + 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; + + if (i == 0) + rule->first_special = tokens; + + rule->data[tokens++] = value; /* The non-terminal */ + rule->data[tokens++] = TOKEN_CPAREN; + } + } + + return rule; +} + + +static parse_rule_t * +_vsatisfy_rule(parse_rule_t *rule, result_word_t *input) +{ + int dep; + + if (!rule->specials_nr) + return NULL; + + dep = rule->data[rule->first_special]; + + if (((dep & TOKEN_TERMINAL_CLASS) + && ((dep & 0xffff) & input->w_class)) + || + ((dep & TOKEN_TERMINAL_GROUP) + && ((dep & 0xffff) & input->group))) { + parse_rule_t *retval = (parse_rule_t*)sci_malloc(sizeof(int) * (4 + rule->length)); + ++_allocd_rules; + retval->id = rule->id; + retval->specials_nr = rule->specials_nr - 1; + retval->length = rule->length; + memcpy(retval->data, rule->data, sizeof(int) * retval->length); + retval->data[rule->first_special] = TOKEN_STUFFING_WORD | input->group; + retval->first_special = 0; + + if (retval->specials_nr) { /* find first special, if it exists */ + int tmp, i = rule->first_special; + + while ((i < rule->length) + && ((tmp = retval->data[i]) & TOKEN_NON_NT) + && !(tmp & TOKEN_TERMINAL)) + ++i; + + if (i < rule->length) + retval->first_special = i; + } + + return retval; + } + else return NULL; +} + +/************** Rule lists **************/ + +void +vocab_free_rule_list(parse_rule_list_t *list) +{ + if (list) { + _vfree(list->rule); + vocab_free_rule_list(list->next); /* Yep, this is slow and memory-intensive. */ + free(list); + } +} + +static inline int +_rules_equal_p(parse_rule_t *r1, parse_rule_t *r2) +{ + if ((r1->id != r2->id) + || (r1->length != r2->length) + || (r1->first_special != r2->first_special)) + return 0; + + return !(memcmp(r1->data, r2->data, sizeof(int) * r1->length)); +} + +static parse_rule_list_t * +_vocab_add_rule(parse_rule_list_t *list, parse_rule_t *rule) +{ + parse_rule_list_t *new_elem; + int term; + + if (!rule) + return list; + + new_elem = (parse_rule_list_t*)sci_malloc(sizeof(parse_rule_list_t)); + term = rule->data[rule->first_special]; + + new_elem->rule = rule; + new_elem->next = NULL; + new_elem->terminal = term = ((term & TOKEN_TERMINAL)? term : 0); + + if (!list) + return new_elem; + else/* if (term < list->terminal) { + new_elem->next = list; + return new_elem; + } else*/ { + parse_rule_list_t *seeker = list; + + while (seeker->next/* && seeker->next->terminal <= term*/) { + if (seeker->next->terminal == term) + if (_rules_equal_p(seeker->next->rule, rule)) { + _vfree(rule); + free(new_elem); + return list; /* No duplicate rules */ + } + seeker = seeker->next; + } + + new_elem->next = seeker->next; + seeker->next = new_elem; + return list; + } +} + +static void +_vprl(parse_rule_list_t *list, int pos) +{ + if (list) { + sciprintf("R%03d: ", pos); + vocab_print_rule(list->rule); + sciprintf("\n"); + _vprl(list->next, pos+1); + } else { + sciprintf("%d rules total.\n", pos); + } +} + +void +vocab_print_rule_list(parse_rule_list_t *list) +{ + _vprl(list, 0); +} + +static parse_rule_list_t * +_vocab_split_rule_list(parse_rule_list_t *list) +{ + if (!list->next + || (list->next->terminal)) { + parse_rule_list_t *tmp = list->next; + list->next = NULL; + return tmp; + } + else return _vocab_split_rule_list(list->next); +} + +static void +_vocab_free_empty_rule_list(parse_rule_list_t *list) +{ + if (list->next) + _vocab_free_empty_rule_list(list->next); + + free(list); +} + +static parse_rule_list_t * +_vocab_merge_rule_lists(parse_rule_list_t *l1, parse_rule_list_t *l2) +{ + parse_rule_list_t *retval = l1, *seeker = l2; + while (seeker) { + retval = _vocab_add_rule(retval, seeker->rule); + seeker = seeker->next; + } + _vocab_free_empty_rule_list(l2); + + return retval; +} + +static int +_vocab_rule_list_length(parse_rule_list_t *list) +{ + return ((list)? _vocab_rule_list_length(list->next) + 1 : 0); +} + + +static parse_rule_list_t * +_vocab_clone_rule_list_by_id(parse_rule_list_t *list, int id) +{ + parse_rule_list_t *result = NULL; + parse_rule_list_t *seeker = list; + + while (seeker) { + if (seeker->rule->id == id) { + result = _vocab_add_rule(result, _vdup(seeker->rule)); + } + seeker = seeker->next; + } + + return result; +} + + +parse_rule_list_t * +_vocab_build_gnf(parse_tree_branch_t *branches, int branches_nr, int verbose) +{ + int i; + int iterations = 0; + int last_termrules, termrules = 0; + int ntrules_nr; + parse_rule_list_t *ntlist = NULL; + parse_rule_list_t *tlist, *new_tlist; + + for (i = 1; i < branches_nr; i++) { /* branch rule 0 is treated specially */ + parse_rule_t *rule = _vbuild_rule(branches + i); + + if (!rule) return NULL; + ntlist = _vocab_add_rule(ntlist, rule); + } + + tlist = _vocab_split_rule_list(ntlist); + ntrules_nr = _vocab_rule_list_length(ntlist); + + if (verbose) + sciprintf("Starting with %d rules\n", ntrules_nr); + + new_tlist = tlist; + tlist = NULL; + + do { + parse_rule_list_t *new_new_tlist = NULL; + parse_rule_list_t *ntseeker, *tseeker; + last_termrules = termrules; + + ntseeker = ntlist; + while (ntseeker) { + tseeker = new_tlist; + + while (tseeker) { + parse_rule_t *newrule = _vinsert(ntseeker->rule, tseeker->rule); + if (newrule) + new_new_tlist = _vocab_add_rule(new_new_tlist, newrule); + tseeker = tseeker->next; + } + + ntseeker = ntseeker->next; + } + + tlist = _vocab_merge_rule_lists(tlist, new_tlist); + + new_tlist = new_new_tlist; + + termrules = _vocab_rule_list_length(new_new_tlist); + + if (verbose) + sciprintf("After iteration #%d: %d new term rules\n", ++iterations, termrules); + } while (termrules && (iterations < 30)); + + vocab_free_rule_list(ntlist); + + if (verbose) { + sciprintf("\nGNF rules:\n"); + vocab_print_rule_list(tlist); + } + + return tlist; +} + +parse_rule_list_t * +vocab_build_gnf(parse_tree_branch_t *branches, int branches_nr) +{ + return _vocab_build_gnf(branches, branches_nr, 0); +} + + +void +vocab_gnf_dump(parse_tree_branch_t *branches, int branches_nr) +{ + parse_rule_list_t *tlist = _vocab_build_gnf(branches, branches_nr, 1); + + sciprintf("%d allocd rules\n", _allocd_rules); + vocab_free_rule_list(tlist); +} + + +int +vocab_build_parse_tree(parse_tree_node_t *nodes, result_word_t *words, int words_nr, + parse_tree_branch_t *branch0, parse_rule_list_t *rules) +{ + return vocab_gnf_parse(nodes, words, words_nr, branch0, rules, 0); +} + + +static int +_vbpt_pareno(parse_tree_node_t *nodes, int *pos, int base) +/* Opens parentheses */ +{ + nodes[base].content.branches[0] = (*pos)+1; + nodes[++(*pos)].type = PARSE_TREE_NODE_BRANCH; + nodes[*pos].content.branches[0] = 0; + nodes[*pos].content.branches[1] = 0; + return *pos; +} + + +static int +_vbpt_parenc(parse_tree_node_t *nodes, int *pos, int paren) +/* Closes parentheses for appending */ +{ + nodes[paren].content.branches[1] = ++(*pos); + nodes[*pos].type = PARSE_TREE_NODE_BRANCH; + nodes[*pos].content.branches[0] = 0; + nodes[*pos].content.branches[1] = 0; + return *pos; +} + + +static int +_vbpt_append(parse_tree_node_t *nodes, int *pos, int base, int value) +/* writes one value to an existing base node and creates a successor node for writing */ +{ + nodes[base].content.branches[0] = ++(*pos); + nodes[*pos].type = PARSE_TREE_NODE_LEAF; + nodes[*pos].content.value = value; + nodes[base].content.branches[1] = ++(*pos); + nodes[*pos].type = PARSE_TREE_NODE_BRANCH; + nodes[*pos].content.branches[0] = 0; + nodes[*pos].content.branches[1] = 0; + return *pos; +} + + +static int +_vbpt_terminate(parse_tree_node_t *nodes, int *pos, int base, int value) + /* Terminates, overwriting a nextwrite forknode */ +{ + nodes[base].type = PARSE_TREE_NODE_LEAF; + nodes[base].content.value = value; + return *pos; +} + +static int +_vbpt_write_subexpression(parse_tree_node_t *nodes, int *pos, + parse_rule_t *rule, int rulepos, int writepos) +{ + int token; + while ((token = ((rulepos < rule->length)? rule->data[rulepos++] : TOKEN_CPAREN)) != TOKEN_CPAREN) { + int nexttoken = (rulepos < rule->length)? rule->data[rulepos] : TOKEN_CPAREN; + if (token == TOKEN_OPAREN) { + int wpold; + int writepos2 = _vbpt_pareno(nodes, pos, wpold = writepos); + rulepos = _vbpt_write_subexpression(nodes, pos, rule, rulepos, writepos2); + nexttoken = (rulepos < rule->length)? rule->data[rulepos] : TOKEN_CPAREN; + if (nexttoken != TOKEN_CPAREN) + writepos = _vbpt_parenc(nodes, pos, wpold); + } else if (token & TOKEN_STUFFING_WORD) { + if (nexttoken == TOKEN_CPAREN) + writepos = _vbpt_terminate(nodes, pos, writepos, token & 0xffff); + else + writepos = _vbpt_append(nodes, pos, writepos, token & 0xffff); + } else { + sciprintf("\nError in parser (grammar.c, _vbpt_write_subexpression()): Rule data broken in rule "); + vocab_print_rule(rule); + sciprintf(", at token position %d\n", *pos); + return rulepos; + } + } + + return rulepos; +} + +int +vocab_gnf_parse(parse_tree_node_t *nodes, result_word_t *words, int words_nr, + parse_tree_branch_t *branch0, parse_rule_list_t *tlist, int verbose) +{ + /* Get the start rules: */ + parse_rule_list_t *work = _vocab_clone_rule_list_by_id(tlist, branch0->data[1]); + parse_rule_list_t *results = NULL; + int word; + + for (word = 0; word < words_nr; word++) { + parse_rule_list_t *new_work = NULL; + parse_rule_list_t *reduced_rules = NULL; + parse_rule_list_t *seeker, *subseeker; + + if (verbose) + sciprintf("Adding word %d...\n", word); + + seeker = work; + while (seeker) { + + if (seeker->rule->specials_nr <= (words_nr - word)) + reduced_rules = _vocab_add_rule(reduced_rules, _vsatisfy_rule(seeker->rule, words + word)); + + seeker = seeker->next; + } + + if (reduced_rules == NULL) { + vocab_free_rule_list(work); + if (verbose) + sciprintf("No results.\n"); + return 1; + } + + vocab_free_rule_list(work); + + if (word +1 < words_nr) { + seeker = reduced_rules; + + while (seeker) { + if (seeker->rule->specials_nr) { + int my_id = seeker->rule->data[seeker->rule->first_special]; + + subseeker = tlist; + while (subseeker) { + if (subseeker->rule->id == my_id) + new_work = _vocab_add_rule(new_work, _vinsert(seeker->rule, subseeker->rule)); + + subseeker = subseeker->next; + } + } + + seeker = seeker->next; + } + vocab_free_rule_list(reduced_rules); + } else /* last word */ + new_work = reduced_rules; + + work = new_work; + if (verbose) + sciprintf("Now at %d candidates\n", _vocab_rule_list_length(work)); + if (work == NULL) { + if (verbose) + sciprintf("No results.\n"); + return 1; + } + } + + results = work; + + if (verbose) { + sciprintf("All results (excluding the surrounding '(141 %03x' and ')'):\n", + branch0->id); + vocab_print_rule_list(results); + sciprintf("\n"); + } + + /* now use the first result */ + { + int temp, pos; + + nodes[0].type = PARSE_TREE_NODE_BRANCH; + nodes[0].content.branches[0] = 1; + nodes[0].content.branches[1] = 2; + + nodes[1].type = PARSE_TREE_NODE_LEAF; + nodes[1].content.value = 0x141; + + nodes[2].type = PARSE_TREE_NODE_BRANCH; + nodes[2].content.branches[0] = 0; + nodes[2].content.branches[1] = 0; + + pos = 2; + + temp = _vbpt_append(nodes, &pos, 2, branch0->id); + /* _vbpt_write_subexpression(nodes, &pos, results[_vocab_rule_list_length(results)].rule, 0, temp); */ + _vbpt_write_subexpression(nodes, &pos, results->rule, 0, temp); + } + + vocab_free_rule_list(results); + return 0; +} diff --git a/engines/sci/engine/heap.c b/engines/sci/engine/heap.c new file mode 100644 index 0000000000..cb5cacaffd --- /dev/null +++ b/engines/sci/engine/heap.c @@ -0,0 +1,298 @@ +#include +#include +#include + +#include +#include +#include "heap.h" + +#define assert_in_range(pos) assert(pos>=1000 && pos<=0xffff) + +static void set_size(heap_t *h, int block_pos, int size) +{ + assert_in_range(block_pos); + assert(size<=0xffff-1000); + putInt16(h->start+block_pos, size); +} + +static void set_next(heap_t* h, int block_pos, int next) +{ + assert_in_range(block_pos); + assert_in_range(next); + putInt16(h->start+block_pos+2, next); +} + + +static unsigned int get_size(heap_t* h, int block_pos) +{ + assert_in_range(block_pos); + return (guint16)getInt16(h->start+block_pos); +} + +static unsigned int get_next(heap_t* h, int block_pos) +{ + assert_in_range(block_pos); + return (guint16)getInt16(h->start+block_pos+2); +} + +/*Allocates a new heap*/ +heap_t* heap_new() +{ + heap_t* h; + if((h= (heap_t*)sci_malloc(sizeof(heap_t)))==0) return 0; + + if((h->start= sci_calloc(SCI_HEAP_SIZE, 1))==0) + { + free(h); + return 0; + } + + h->base=h->start+1000; + h->first_free=1000; + h->old_ff=-1; + set_size(h, 1000, 0xffff-1000); + set_next(h, 1000, 0xffff); + + return h; +} + +/*Deletes a heap*/ +void heap_del(heap_t* h) +{ + free(h->start); + free(h); +} + + +int heap_meminfo(heap_t* h) +{ + heap_ptr current = h->first_free; + int total = 0; + + while (current != 0xffff) { + total += get_size(h, current); + current = get_next(h, current); + } + + return total; +} + + +int heap_largest(heap_t* h) +{ + int current=h->first_free; + int best_pos=-1, best_size=0; + + while(current!=0xffff) + { + int size=get_size(h, current); + int next=get_next(h, current); + + if(size>best_size) + { + best_pos=current; + best_size=size; + } + + current=next; + } + + return best_size; +} + +heap_ptr heap_allocate(heap_t* h, int size) +{ + unsigned int previous=h->first_free; + unsigned int current=previous; + + if (!size) { + fprintf(stderr,"Warning: heap_alloc'd zero bytes!\n"); + size += 2; + } + + size+=2+(size&1); + + while(current<0xffff) + { + int block_size=get_size(h, current); + int next=get_next(h, current); + + /*Is this block large enough?*/ + if(block_size>=size) + { + /*Swallow the block whole*/ + if(block_size<=size+4) + { + size=block_size; + set_next(h, previous, next); + } + else { + /*Split the block*/ + int rest=current+size; + + set_next(h, previous, rest); + set_size(h, rest, block_size-size); + set_next(h, rest, next); + next=rest; + } + set_size(h, current, size); + if(current==h->first_free) h->first_free=next; + return current; + } + previous=current; + current=next; + } + + /*No large enough block was found.*/ + return 0; +} + +void heap_free(heap_t* h, unsigned int m) +{ + unsigned int previous, next; + assert_in_range(m); + previous=next=h->first_free; + + /*Find the previous and next blocks*/ + while(next < m) + { + previous = next; + assert(previous<0xffff); + next=get_next(h, previous); + if (next <= previous) { + sciprintf("Heap corrupt. Aborting heap_free()...\n"); + return; + } + } + + if (h->first_free > m) + h->first_free = m; /* Guarantee that first_free is correct */ + + if(previous==next) + { + if(mfirst_free=m; + if(m+get_size(h, m)==previous) + { + set_size(h, m, get_size(h, m)+get_size(h, previous)); + set_next(h, m, get_next(h, previous)); + } + else set_next(h, m, previous); + } + else + { + if(previous+get_size(h, previous)==m) + { + set_size(h, previous, get_size(h, previous)+get_size(h, m)); + set_next(h, previous, 0xffff); + } + else + { + set_next(h, previous, m); + set_next(h, m, next); + } + } + } + else + { + set_next(h, previous, m); + set_next(h, m, next); + + /*Try to merge with previous*/ + if(previous+get_size(h, previous)==m) + { + set_size(h, previous, get_size(h, previous)+get_size(h, m)); + set_next(h, previous, next); + m=previous; + } + + /*Try to merge with next*/ + if(m+get_size(h, m)==next) + { + set_size(h, m, get_size(h, m)+get_size(h, next)); + set_next(h, m, get_next(h, next)); + } + } +} + +void save_ff(heap_t* h) +{ + h->old_ff=h->first_free; +} + +void restore_ff(heap_t* h) +{ + h->first_free=h->old_ff; + set_size(h, h->first_free, 0xffff-h->first_free); + set_next(h, h->first_free, 0xffff); +} + + + +void heap_dump_free(heap_t *h) +{ + int freedomseeker; + + printf("\tfirst_free= %#x (oldff= %#x)\n\tFree Blocks:\n", h->first_free, h->old_ff); + + freedomseeker = h->first_free; + while (freedomseeker != 0xffff) { + printf("\t %#04x: size: %#04x\n", freedomseeker, get_size(h, freedomseeker)); + freedomseeker = get_next(h, freedomseeker); + } +} + +void heap_dump_all(heap_t *h) +{ + int seeker = 1000; + int free_seeker = h->first_free; + + while (seeker < 0xffff) { + int is_free = (seeker == free_seeker); + int size = get_size(h, seeker); + + if (is_free) + free_seeker = get_next(h, free_seeker); + + printf("%04x\t%d\t%s\n", seeker, size, is_free? "FREE": ""); + seeker += size; + } +} + +/* + +int main(int argc, char **argv) { + heap_t *h = heap_new(); + int a,b,c,d,e; + + printf("Running heap tests:\nHeap initialization:\n"); + heap_dump_free(h); + + printf("[a] Allocating 0x1: position is %#x\n", a = heap_allocate(h, 1)); + heap_dump_free(h); + + printf("[b] Allocating 0x10: position is %#x\n", b = heap_allocate(h, 0x10)); + printf("[c] Allocating 0x10: position is %#x\n", c = heap_allocate(h, 0x10)); + printf("[d] Allocating 0x10: position is %#x\n", d = heap_allocate(h, 0x10)); + printf("[e] Allocating 0x1000: position is %#x\n", e = heap_allocate(h, 0x1000)); + heap_dump_free(h); + + printf("Freeing [b]:\n"); + heap_free(h, b); + heap_dump_free(h); + + printf("Freeing [d]:\n"); + heap_free(h, d); + heap_dump_free(h); + + printf("Freeing [c]:\n"); + heap_free(h, c); + heap_dump_free(h); + + heap_del(h); + + return 0; +} + +*/ diff --git a/engines/sci/engine/heap.h b/engines/sci/engine/heap.h new file mode 100644 index 0000000000..afd2db5c58 --- /dev/null +++ b/engines/sci/engine/heap.h @@ -0,0 +1,125 @@ +/*************************************************************************** + heap.h Copyright (C) 1999,2000,01 Magnus Reftel, Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + +#ifndef _SCI_HEAP_H +#define _SCI_HEAP_H + +#include + +#define SCI_HEAP_SIZE 0x10000 + +typedef guint16 heap_ptr; + + +typedef struct +{ + byte* start; + byte* base; + unsigned int first_free; + int old_ff; +} heap_t; + +heap_t* +heap_new(); +/* Allocates a new heap. +** Parameters: (void) +** Returns : (heap_t *) A new 0xffff-sized heap +*/ + +void +heap_del(heap_t* h); +/* Frees an allocated heap +** Parameters: (heap_t *) h: The heap to unallocate +** Returns : (void) +*/ + +int +heap_meminfo(heap_t* h); +/* Returns the total number of free bytes on the heap +** Parameters: (heap_t *) h: The heap to check +** Returns : (int) The total free space in bytes +*/ + +int +heap_largest(heap_t* h); +/* Returns the block size of the largest free block on the heap +** Parameters: (heap_t *) h: The heap to check +** Returns : (int) The size of the largest free block +*/ + +heap_ptr +heap_allocate(heap_t* h, int size); +/* Allocates memory on a heap. +** Parameters: (heap_t *) h: The heap to work with +** (int) size: The block size to allocate +** Returns : (heap_ptr): The heap pointer to the new block, or 0 on failure +*/ + +void +heap_free(heap_t* h, unsigned int m); +/* Frees allocated heap memory. +** Parameters: (heap_t *) h: The heap to work with +** (int) m: The handle at which memory is to be unallocated +** Returns : (void) +** This function automatically prevents fragmentation from happening. +*/ + +void +save_ff(heap_t* h); +/* Stores the current first free position +** Parameters: (heap_t *) h: The heap which is to be manipulated +** Returns : (void) +** This function can be used to save the heap state for later restoration (see +** the next function) +*/ + +void +restore_ff(heap_t* h); +/* Restores the first free heap state +** Parameters: (heap_t *) h: The heap to restore +** Returns : (void) +** Restoring the first free state will reset the heap to the position stored +** when save_ff() was called, if and only if none of the blocks allocated before +** save_ff() was called was ever freed ("ever" includes "before save_ff() was +** called"). +*/ + +void +heap_dump_free(heap_t *h); +/* Dumps debugging information about the stack +** Parameters: (heap_t *) h: The heap to check +** Returns : (void) +*/ + +void +heap_dump_all(heap_t *h); +/* Dumps all allocated/unallocated zones on the heap +** Parameters: (heap_t *) h: The heap to check +** Returns : (void) +*/ + +#endif /* !_SCI_HEAP_H */ diff --git a/engines/sci/engine/kemu_old.c b/engines/sci/engine/kemu_old.c new file mode 100644 index 0000000000..4ec6c0a106 --- /dev/null +++ b/engines/sci/engine/kemu_old.c @@ -0,0 +1,330 @@ +/*************************************************************************** + kemu_old.c Copyright (C) 2002 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Emulation code for old kernel functions */ + +#include +#include "kernel_compat.h" +#include "kernel_types.h" +#include "heap.h" + +#define EMU_HEAP_START_ADDR 1000 + +#define EMU_TYPE_VERBATIM 0 /* Arithmetic values */ +#define EMU_TYPE_REGISTERS 1 /* Must re-interpret afterwards. Also used for objects. */ +#define EMU_TYPE_BLOCK 2 /* Bulk data; string or similar. May physically point to stack */ + +static int funct_nr; + +typedef struct { + heap_ptr start; /* Beginning of the block */ + reg_t pos; /* Needed for resolving conflicts */ + int length; /* Number of bytes occupied on the heap */ + object_t *obj; /* For object types: Pointer to the physical object */ + + /* Emulation data part */ + int emudat_type; /* EMU_TYPE_... */ + heap_ptr emudat_location; /* Only for 1 and 2 */ + union { + reg_t *regs; /* registers, for type 1 */ + byte *block; /* Bulk data location, for type 2 */ + } emu_data; + int emudat_size; /* Amount of bytes or number of entries */ +} emu_param_t; + +static inline emu_param_t +identify_value(state_t *s, reg_t reg, heap_ptr firstfree) +{ + emu_param_t retval; + int type = determine_reg_type(s, reg); + + retval.start = firstfree; + retval.pos = reg; + + retval.obj = NULL; + retval.length = 0; + retval.emudat_type = EMU_TYPE_VERBATIM; /* To allow error aborts */ + + if (type & KSIG_NULL) + type &= KSIG_ARITHMETIC; + + switch (type) { + + case KSIG_ARITHMETIC: /* trivial */ + retval.emudat_size = 0; + break; + + case KSIG_OBJECT: + retval.emudat_type = EMU_TYPE_REGISTERS; + retval.obj = obj_get(s, reg); + if (!retval.obj) { BREAKPOINT(); } + retval.length = -SCRIPT_OBJECT_MAGIC_OFFSET + + retval.obj->variables_nr * 4 /* values and selectors */ + + 2; /* Extra magic selector word (is this really needed?) */ + retval.emu_data.regs = retval.obj->variables; + retval.emudat_size = retval.obj->variables_nr; + retval.emudat_location = retval.start - SCRIPT_OBJECT_MAGIC_OFFSET; + break; + + case KSIG_REF: { + retval.emudat_type = EMU_TYPE_BLOCK; + + retval.emu_data.block = + s->seg_manager.dereference(&s->seg_manager, + reg, &retval.emudat_size); + + if (!retval.emu_data.block) { + SCIkdebug(SCIkERROR, "Cannot handle references into segment type %02x" + " (from "PREG") during emulation\n", + type, PRINT_REG(reg)); + retval.emudat_type = EMU_TYPE_VERBATIM; + retval.length = 0; + } + + retval.length = retval.emudat_size; + retval.emudat_location = retval.start; + + break; + } + + default: + SCIkdebug(SCIkERROR, "Cannot handle argument type %02x (from "PREG + ") during emulation\n", + type, PRINT_REG(reg)); + } + + return retval; +} + +static inline void +writeout_value(state_t *s, emu_param_t *p) +{ + if (p->obj) /* First copy object; don't need to read back this part later */ + memcpy(s->heap + p->start, p->obj->base_obj + SCRIPT_OBJECT_MAGIC_OFFSET, + p->length); + + switch (p->emudat_type) { + + case EMU_TYPE_VERBATIM: + SCIkdebug(SCIkEMU, "\tVerbatim/Arithmetic\n"); + return; + + case EMU_TYPE_REGISTERS: { + int i, max = p->emudat_size; + + SCIkdebug(SCIkEMU, "\tObject, %d selectors\n", max); + + for (i = 0; i < max; i++) { + byte *dest = s->heap + p->emudat_location + (i << 1); + + dest[0] = p->emu_data.regs[i].offset & 0xff; + dest[1] = (p->emu_data.regs[i].offset >> 8) & 0xff; + /* Assume they're all numeric values */ + } + return; + } + + case EMU_TYPE_BLOCK: + SCIkdebug(SCIkEMU, "\tBulk\n"); + memcpy(s->heap + p->emudat_location, p->emu_data.block, p->emudat_size); + return; + + default: + BREAKPOINT(); + } +} + +static inline void +recover_value(state_t *s, emu_param_t *p) +{ /* Writes back the value */ + switch (p->emudat_type) { + + case EMU_TYPE_VERBATIM: + return; + + case EMU_TYPE_REGISTERS: { + int i, max = p->emudat_size; + for (i = 0; i < max; i++) { + int val = GET_HEAP(p->emudat_location + (i << 1)); + if (p->emu_data.regs[i].offset != val) { + SCIkdebug(SCIkEMU, " Recovering property #%d from %04x: 0:%04x\n", + i, p->emudat_location + (i << 1), val); + p->emu_data.regs[i] = make_reg(0, val); + } else { + SCIkdebug(SCIkEMU, " Property #%d from %04x is unchanged (%04x vs "PREG")\n", + i, p->emudat_location + (i << 1), val, PRINT_REG(p->emu_data.regs[i])); + } + /* Don't overwrite unless something changed, to preserve pointers */ + } + return; + } + + case EMU_TYPE_BLOCK: + memcpy(p->emu_data.block, s->heap + p->emudat_location, p->emudat_size); + SCIkdebug(SCIkEMU, " Recovering %d raw bytes from %04x\n", + p->emudat_size, p->emudat_location); + return; + + default: + BREAKPOINT(); + } +} + +static void +_restrict_against(state_t *s, emu_param_t *self, emu_param_t *other) +{ + if (self->pos.segment != other->pos.segment) + return; + + if (self->pos.offset <= other->pos.offset + && self->pos.offset + self->emudat_size > other->pos.offset) { + mem_obj_t *mobj = GET_SEGMENT_ANY(s->seg_manager, self->pos.segment); + + self->emudat_size = other->pos.offset - self->pos.offset; + + if (mobj && mobj->type == MEM_OBJ_STACK) + self->emudat_size *= sizeof(reg_t); /* Accomodate for size differences */ + } +} + +static void +resolve_conflicts(state_t *s, emu_param_t *params, int count) +{ + int i, j; + for (i = 0; i < count; i++) + for (j = 0; j < count; j++) + if (i != j) + _restrict_against(s, params + i, params + j); +} + +reg_t +kFsciEmu(state_t *s, int _funct_nr, int argc, reg_t *argv) +{ + emu_param_t *shadow_args; + funct_nr = _funct_nr; + + if (!s->kfunct_emu_table[funct_nr]) { + SCIkwarn(SCIkERROR, "Attempt to emulate unknown kernel function %x\n", + funct_nr); + return NULL_REG; + } else { + heap_ptr argp = EMU_HEAP_START_ADDR; /* arguments go here */ + heap_ptr datap = argp + argc * 2; /* copied stuff goes here */ + int i; + + shadow_args = sci_malloc(sizeof(emu_param_t) * (argc + 1 + /* Prevents stupid warning */)); + memset(shadow_args, 0, sizeof(emu_param_t) * (argc + 1)); + + SCIkdebug(SCIkEMU, "Emulating kernel call %s[%x] w/ %d arguments at HP:%04x\n", + s->kernel_names[funct_nr], funct_nr, argc, argp); + + for (i = 0; i < argc; i++) { + int emu_value = argv[i].offset; /* Value we'll pass to the function */ + + SCIkdebug(SCIkEMU, " %3d : ["PREG"] ->\n", i, PRINT_REG(argv[i])); + + shadow_args[i] = identify_value(s, argv[i], datap); + + switch (shadow_args[i].emudat_type) { + + case EMU_TYPE_VERBATIM: + break; + + case EMU_TYPE_REGISTERS: + case EMU_TYPE_BLOCK: + emu_value = shadow_args[i].emudat_location; + break; + + default: + BREAKPOINT(); + + } + + SCIkdebug(SCIkEMU, "\t%04x [%d at %04x]\n", + emu_value, shadow_args[i].length, shadow_args[i].start); + + PUT_HEAP(argp, emu_value); + argp += 2; + + if (0xffff - shadow_args[i].length < datap) { + SCIkdebug(SCIkERROR, "Out of heap space while emulating!\n"); + return NULL_REG; + } + datap += shadow_args[i].length; /* Step over last block we wrote */ + } + + for (i = 0; i < argc; i++) + writeout_value(s, shadow_args + i); + + resolve_conflicts(s, shadow_args, argc); + + s->kfunct_emu_table[funct_nr](s, funct_nr, argc, EMU_HEAP_START_ADDR); + + for (i = 0; i < argc; i++) + recover_value(s, shadow_args + i); + free(shadow_args); + return make_reg(0, s->acc); + } +} + + +int +read_selector16(state_t *s, heap_ptr object, selector_t selector_id, const char *file, int line) +{ + int slc_count = GET_HEAP(object + SCRIPT_SELECTORCTR_OFFSET); + int i; + heap_ptr slc_names = object + slc_count * 2; + + for (i = 0; i < slc_count; i++) { + if (GET_HEAP(slc_names + (i<<1)) == selector_id) + return GET_HEAP(object + (i<<1)); + } + + SCIkdebug(SCIkWARNING, "[EMU] Could not read selector %d from HP:%04x (%s, L%d)\n", + selector_id, object, file, line); + return 0; +} + +void +write_selector16(state_t *s, heap_ptr object, selector_t selector_id, + int value, const char *fname, int line) +{ + int slc_count = GET_HEAP(object + SCRIPT_SELECTORCTR_OFFSET); + int i; + heap_ptr slc_names = object + slc_count * 2; + + for (i = 0; i < slc_count; i++) { + if (GET_HEAP(slc_names + (i<<1)) == selector_id) { + PUT_HEAP(object + (i<<1), value); + return; + } + } + + SCIkdebug(SCIkWARNING, "[EMU] Could not write selector %d from HP:%04x (%s, L%d)\n", + selector_id, object, fname, line); +} + diff --git a/engines/sci/engine/kernel.c b/engines/sci/engine/kernel.c new file mode 100644 index 0000000000..df3690f467 --- /dev/null +++ b/engines/sci/engine/kernel.c @@ -0,0 +1,1092 @@ +/*************************************************************************** + kernel.c Copyright (C) 1999 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + +#include "gc.h" +#include +#include +#ifdef _WIN32 +# include +#endif /* _WIN32 */ + +#include +#include "kernel_types.h" + + +/* New kernel functions */ +reg_t kStrLen(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kGetFarText(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kReadNumber(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kStrCat(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kStrCmp(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kSetSynonyms(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kLock(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kPalette(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kNumCels(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kNumLoops(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kDrawCel(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kCoordPri(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kPriCoord(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kShakeScreen(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kSetCursor(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kMoveCursor(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kShow(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kPicNotValid(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kOnControl(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kDrawPic(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kGetPort(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kSetPort(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kNewWindow(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kDisposeWindow(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kCelWide(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kCelHigh(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kSetJump(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kDirLoop(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kDoAvoider(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kGetAngle(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kGetDistance(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kRandom(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kAbs(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kSqrt(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kTimesSin(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kTimesCos(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kCosMult(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kSinMult(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kTimesTan(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kTimesCot(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kCosDiv(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kSinDiv(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kValidPath(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kFOpen(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kFPuts(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kFGets(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kFClose(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kMapKeyToDir(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kGlobalToLocal(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kLocalToGlobal(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kWait(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kRestartGame(struct _state *s, int funct_nr, int argc, reg_t *argv); +#ifdef _WIN32 +reg_t kDeviceInfo_Win32(struct _state *s, int funct_nr, int argc, reg_t *argv); +#else +reg_t kDeviceInfo_Unix(struct _state *s, int funct_nr, int argc, reg_t *argv); +#endif +reg_t kGetEvent(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kCheckFreeSpace(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kFlushResources(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kGetSaveFiles(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kSetDebug(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kCheckSaveGame(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kSaveGame(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kRestoreGame(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kFileIO(struct _state *s, int funct_nr, int argc, reg_t *argp); +reg_t kGetTime(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kHaveMouse(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kJoystick(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kGameIsRestarting(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kGetCWD(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kSort(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kStrEnd(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kMemory(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kAvoidPath(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kParse(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kSaid(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kStrCpy(struct _state *s, int funct_nr, int argc, reg_t *argp); +reg_t kStrAt(struct _state *s, int funct_nr, int argc, reg_t *argp); +reg_t kEditControl(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kDrawControl(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kHiliteControl(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kClone(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kDisposeClone(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kCanBeHere(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kSetNowSeen(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kInitBresen(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kDoBresen(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kBaseSetter(struct _state *s, int funct_nr, int argc, reg_t *argp); +reg_t kAddToPic(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kAnimate(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kDisplay(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kGraph(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kFormat(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kDoSound(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kAddMenu(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kSetMenu(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kGetMenu(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kDrawStatus(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kDrawMenuBar(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kMenuSelect(struct _state *s, int funct_nr, int argc, reg_t *argv); + +reg_t kLoad(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kUnLoad(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kScriptID(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kDisposeScript(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kIsObject(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kRespondsTo(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kNewList(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kDisposeList(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kNewNode(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kFirstNode(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kLastNode(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kEmptyList(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kNextNode(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kPrevNode(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kNodeValue(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kAddAfter(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kAddToFront(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kAddToEnd(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kFindKey(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kDeleteKey(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kMemoryInfo(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kGetSaveDir(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kTextSize(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kIsItSkip(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kMessage(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t kDoAudio(struct _state *s, int funct_nr, int argc, reg_t *argv); +reg_t k_Unknown(struct _state *s, int funct_nr, int argc, reg_t *argv); + +/* The Unknown/Unnamed kernel function */ +reg_t kstub(struct _state *s, int funct_nr, int argc, reg_t *argv); +/* for unimplemented kernel functions */ +reg_t kNOP(struct _state *s, int funct_nr, int argc, reg_t *argv); +/* for kernel functions that don't do anything */ +reg_t kFsciEmu(struct _state *s, int funct_nr, int argc, reg_t *argv); +/* Emulating "old" kernel functions on the heap */ + + +#define SCI_MAPPED_UNKNOWN_KFUNCTIONS_NR 0x75 +/* kfunct_mappers below doubles for unknown kfunctions */ + +static int sci_max_allowed_unknown_kernel_functions[] = { + 0, + 0x72, /* SCI0 */ + 0x7b, /* SCI01/EGA */ + 0x7b, /* SCI01/VGA */ + 0x7b, /* SCI1/EARLY */ + 0x7b, /* SCI1/LATE */ + 0x7b, /* SCI1.1 */ + 0x0, /* SCI32 */ +}; + +#define DEFUN(nm, cname, sig) {KF_NEW, nm, {cname, sig, NULL}} +#define NOFUN(nm) {KF_NONE, nm, {NULL, NULL, NULL}} + +sci_kernel_function_t kfunct_mappers[] = { +/*00*/ DEFUN("Load", kLoad, "iii*"), +/*01*/ DEFUN("UnLoad", kUnLoad, "i.*"), +/*02*/ DEFUN("ScriptID", kScriptID, "Ioi*"), +/*03*/ DEFUN("DisposeScript", kDisposeScript, "Oi"), /* Work around QfG1 bug */ +/*04*/ DEFUN("Clone", kClone, "o"), +/*05*/ DEFUN("DisposeClone", kDisposeClone, "o"), +/*06*/ DEFUN("IsObject", kIsObject, "."), +/*07*/ DEFUN("RespondsTo", kRespondsTo, ".i"), +/*08*/ DEFUN("DrawPic", kDrawPic, "i*"), +/*09*/ DEFUN("Show", kShow, "i"), +/*0a*/ DEFUN("PicNotValid", kPicNotValid, "i*"), +/*0b*/ DEFUN("Animate", kAnimate, "LI*"), /* More like (li?)? */ +/*0c*/ DEFUN("SetNowSeen", kSetNowSeen, "oi*"), /* The second parameter is ignored */ +/*0d*/ DEFUN("NumLoops", kNumLoops, "o"), +/*0e*/ DEFUN("NumCels", kNumCels, "o"), +/*0f*/ DEFUN("CelWide", kCelWide, "iOiOi"), +/*10*/ DEFUN("CelHigh", kCelHigh, "iOiOi"), +/*11*/ DEFUN("DrawCel", kDrawCel, "iiiiii*"), +/*12*/ DEFUN("AddToPic", kAddToPic, "Il*"), +/*13*/ DEFUN("NewWindow", kNewWindow, "iiiiZRi*"), +/*14*/ DEFUN("GetPort", kGetPort, ""), +/*15*/ DEFUN("SetPort", kSetPort, "ii*"), +/*16*/ DEFUN("DisposeWindow", kDisposeWindow, "ii*"), +/*17*/ DEFUN("DrawControl", kDrawControl, "o"), +/*18*/ DEFUN("HiliteControl", kHiliteControl, "o"), +/*19*/ DEFUN("EditControl", kEditControl, "ZoZo"), +/*1a*/ DEFUN("TextSize", kTextSize, "rZrii*r*"), +/*1b*/ DEFUN("Display", kDisplay, ".*"), +/*1c*/ DEFUN("GetEvent", kGetEvent, "io"), +/*1d*/ DEFUN("GlobalToLocal", kGlobalToLocal, "o"), +/*1e*/ DEFUN("LocalToGlobal", kLocalToGlobal, "o"), +/*1f*/ DEFUN("MapKeyToDir", kMapKeyToDir, "o"), +/*20*/ DEFUN("DrawMenuBar", kDrawMenuBar, "i"), +/*21*/ DEFUN("MenuSelect", kMenuSelect, "oi*"), +/*22*/ DEFUN("AddMenu", kAddMenu, "rr"), +/*23*/ DEFUN("DrawStatus", kDrawStatus, "Zri*"), +/*24*/ DEFUN("Parse", kParse, "ro"), +/*25*/ DEFUN("Said", kSaid, "Zr"), +/*26*/ DEFUN("SetSynonyms", kSetSynonyms, "o"), +/*27*/ DEFUN("HaveMouse", kHaveMouse, ""), +/*28*/ DEFUN("SetCursor", kSetCursor, "i*"), +/*28*/ DEFUN("MoveCursor", kMoveCursor, "ii*"), +/*29*/ DEFUN("FOpen", kFOpen, "ri"), +/*2a*/ DEFUN("FPuts", kFPuts, "ir"), +/*2b*/ DEFUN("FGets", kFGets, "rii"), +/*2c*/ DEFUN("FClose", kFClose, "i"), +/*2d*/ DEFUN("SaveGame", kSaveGame, "rirr*"), +/*2e*/ DEFUN("RestoreGame", kRestoreGame, "rir*"), +/*2f*/ DEFUN("RestartGame", kRestartGame, ""), +/*30*/ DEFUN("GameIsRestarting", kGameIsRestarting, "i*"), +/*31*/ DEFUN("DoSound", kDoSound, "iIo*"), +/*32*/ DEFUN("NewList", kNewList, ""), +/*33*/ DEFUN("DisposeList", kDisposeList, "l"), +/*34*/ DEFUN("NewNode", kNewNode, ".."), +/*35*/ DEFUN("FirstNode", kFirstNode, "Zl"), +/*36*/ DEFUN("LastNode", kLastNode, "l"), +/*37*/ DEFUN("EmptyList", kEmptyList, "l"), +/*38*/ DEFUN("NextNode", kNextNode, "n!"), +/*39*/ DEFUN("PrevNode", kPrevNode, "n"), +/*3a*/ DEFUN("NodeValue", kNodeValue, "Zn!"), +/*3b*/ DEFUN("AddAfter", kAddAfter, "lnn"), +/*3c*/ DEFUN("AddToFront", kAddToFront, "ln"), +/*3d*/ DEFUN("AddToEnd", kAddToEnd, "ln"), +/*3e*/ DEFUN("FindKey", kFindKey, "l."), +/*3f*/ DEFUN("DeleteKey", kDeleteKey, "l."), +/*40*/ DEFUN("Random", kRandom, "i*"), +/*41*/ DEFUN("Abs", kAbs, "Oi"), +/*42*/ DEFUN("Sqrt", kSqrt, "i"), +/*43*/ DEFUN("GetAngle", kGetAngle, "iiii"), +/*44*/ DEFUN("GetDistance", kGetDistance, "iiiii*"), +/*45*/ DEFUN("Wait", kWait, "i"), +/*46*/ DEFUN("GetTime", kGetTime, "i*"), +/*47*/ DEFUN("StrEnd", kStrEnd, "r"), +/*48*/ DEFUN("StrCat", kStrCat, "rr"), +/*49*/ DEFUN("StrCmp", kStrCmp, "rri*"), +/*4a*/ DEFUN("StrLen", kStrLen, "r"), +/*4b*/ DEFUN("StrCpy", kStrCpy, "rri*"), +/*4c*/ DEFUN("Format", kFormat, "r.*"), +/*4d*/ DEFUN("GetFarText", kGetFarText, "iir"), +/*4e*/ DEFUN("ReadNumber", kReadNumber, "r"), +/*4f*/ DEFUN("BaseSetter", kBaseSetter, "o"), +/*50*/ DEFUN("DirLoop", kDirLoop, "oi"), +/*51*/ DEFUN("CanBeHere", kCanBeHere, "ol*"), +/*51*/ DEFUN("CantBeHere", kCanBeHere, "ol*"), +/*52*/ DEFUN("OnControl", kOnControl, "i*"), +/*53*/ DEFUN("InitBresen", kInitBresen, "oi*"), +/*54*/ DEFUN("DoBresen", kDoBresen, "o"), +/*55*/ DEFUN("DoAvoider", kDoAvoider, "o"), +/*56*/ DEFUN("SetJump", kSetJump, "oiii"), +/*57*/ DEFUN("SetDebug", kSetDebug, "i*"), +/*58*/ NOFUN("InspectObj"), +/*59*/ NOFUN("ShowSends"), +/*5a*/ NOFUN("ShowObjs"), +/*5b*/ NOFUN("ShowFree"), +/*5c*/ DEFUN("MemoryInfo", kMemoryInfo, "i"), +/*5d*/ NOFUN("StackUsage"), +/*5e*/ NOFUN("Profiler"), +/*5f*/ DEFUN("GetMenu", kGetMenu, "i."), +/*60*/ DEFUN("SetMenu", kSetMenu, "i.*"), +/*61*/ DEFUN("GetSaveFiles", kGetSaveFiles, "rrr"), +/*62*/ DEFUN("GetCWD", kGetCWD, "r"), +/*63*/ DEFUN("CheckFreeSpace", kCheckFreeSpace, "r"), +/*64*/ DEFUN("ValidPath", kValidPath, "r"), +/*65*/ DEFUN("CoordPri", kCoordPri, "i"), +/*66*/ DEFUN("StrAt", kStrAt, "rii*"), +#ifdef _WIN32 +/*67*/ DEFUN("DeviceInfo", kDeviceInfo_Win32, "i.*"), +#else /* !_WIN32 */ +/*67*/ DEFUN("DeviceInfo", kDeviceInfo_Unix, "i.*"), +#endif +/*68*/ DEFUN("GetSaveDir", kGetSaveDir, ""), +/*69*/ DEFUN("CheckSaveGame", kCheckSaveGame, ".*"), +/*6a*/ DEFUN("ShakeScreen", kShakeScreen, "ii*"), +/*6b*/ DEFUN("FlushResources", kFlushResources, "i"), +/*6c*/ DEFUN("TimesSin", kTimesSin, "ii"), +/*6d*/ DEFUN("TimesCos", kTimesCos, "ii"), +#if 0 +/*6e*/ NOFUN(NULL), +/*6f*/ NOFUN(NULL), +#else +/*6e*/ DEFUN("6e", kTimesSin, "ii"), +/*6f*/ DEFUN("6f", kTimesCos, "ii"), +#endif +/*70*/ DEFUN("Graph", kGraph, ".*"), +/*71*/ DEFUN("Joystick", kJoystick, ".*"), +/*72*/ NOFUN(NULL), +/*73*/ NOFUN(NULL), + + /* Experimental functions */ +/*74*/ DEFUN("FileIO", kFileIO, "i.*"), +/*(?)*/ DEFUN("Memory", kMemory, "i.*"), +/*(?)*/ DEFUN("Sort", kSort, "ooo"), +/*(?)*/ DEFUN("AvoidPath", kAvoidPath, "ii.*"), +/*(?)*/ DEFUN("Lock", kLock, "iii*"), +/*(?)*/ DEFUN("Palette", kPalette, "i*"), +/*(?)*/ DEFUN("IsItSkip", kIsItSkip, "iiiii"), + + /* Non-experimental Functions without a fixed ID */ + + DEFUN("CosMult", kTimesCos, "ii"), + DEFUN("SinMult", kTimesSin, "ii"), +/*(?)*/ DEFUN("CosDiv", kCosDiv, "ii"), +/*(?)*/ DEFUN("PriCoord", kPriCoord, "i"), +/*(?)*/ DEFUN("SinDiv", kSinDiv, "ii"), +/*(?)*/ DEFUN("TimesCot", kTimesCot, "ii"), +/*(?)*/ DEFUN("TimesTan", kTimesTan, "ii"), +DEFUN("Message", kMessage, ".*"), +DEFUN("DoAudio", kDoAudio, ".*"), + + + /* Special and NOP stuff */ + {KF_NEW, NULL, {k_Unknown, NULL, NULL}}, + + {KF_TERMINATOR, NULL, {NULL, NULL, NULL}} /* Terminator */ +}; + +static const char *argtype_description[] = {"Undetermined (WTF?)", "List","Node","Object","Reference","Arithmetic"}; + + +/******************** Kernel Oops ********************/ + +int +kernel_oops(state_t *s, const char *file, int line, const char *reason) +{ + sciprintf("Kernel Oops in file %s, line %d: %s\n", file, line, reason); + fprintf(stderr,"Kernel Oops in file %s, line %d: %s\n", file, line, reason); + script_debug_flag = script_error_flag = 1; + return 0; +} + + +/* Allocates a set amount of memory for a specified use and returns a handle to it. */ +reg_t +kalloc(state_t *s, const char *type, int space) +{ + reg_t reg; + + sm_alloc_hunk_entry(&s->seg_manager, type, space, ®); + SCIkdebug(SCIkMEM, "Allocated %d at hunk "PREG" (%s)\n", space, PRINT_REG(reg), type); + + return reg; +} + +int +has_kernel_function(state_t *s, const char *kname) +{ + int i = 0; + + while (s->kernel_names[i]) + { + if (!strcmp(s->kernel_names[i], kname)) + return 1; + i++; + } + + return 0; +} + +/* Returns a pointer to the memory indicated by the specified handle */ +byte * +kmem(state_t *s, reg_t handle) +{ + mem_obj_t *mobj = GET_SEGMENT(s->seg_manager, handle.segment, MEM_OBJ_HUNK); + hunk_table_t *ht = &(mobj->data.hunks); + + if (!mobj || !ENTRY_IS_VALID(ht, handle.offset)) { + SCIkwarn(SCIkERROR, "Error: kmem() with invalid handle\n"); + return NULL; + } + + return (byte *) ht->table[handle.offset].entry.mem; +} + +/* Frees the specified handle. Returns 0 on success, 1 otherwise. */ +int +kfree(state_t *s, reg_t handle) +{ + sm_free_hunk_entry(&s->seg_manager, handle); + + return 0; +} + + +/*****************************************/ +/************* Kernel functions **********/ +/*****************************************/ + +char *old_save_dir; + +reg_t +kRestartGame(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char *deref_save_dir = (char*)kernel_dereference_bulk_pointer(s, s->save_dir_copy, 1); + + old_save_dir = strdup(deref_save_dir); + s->restarting_flags |= SCI_GAME_IS_RESTARTING_NOW; + s->restarting_flags &= ~SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE; /* This appears to help */ + s->execution_stack_pos = s->execution_stack_base; + script_abort_flag = 1; /* Force vm to abort ASAP */ + return NULL_REG; +} + + +/* kGameIsRestarting(): +** Returns the restarting_flag in acc +*/ +reg_t +kGameIsRestarting(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char *deref_save_dir = (char*)kernel_dereference_bulk_pointer(s, s->save_dir_copy, 1); + + if (old_save_dir&&deref_save_dir) + { + strcpy(deref_save_dir, old_save_dir); + free(old_save_dir); + old_save_dir = NULL; + } + + s->r_acc = make_reg(0, (s->restarting_flags & SCI_GAME_WAS_RESTARTED)); + + if (argc) {/* Only happens during replay */ + if (!UKPV(0)) /* Set restarting flag */ + s->restarting_flags &= ~SCI_GAME_WAS_RESTARTED; + } + + return s->r_acc; +} + +reg_t +kHaveMouse(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + + return make_reg (0, (s->have_mouse_flag + && gfxop_have_mouse(s->gfx_state))? -1 : 0); +} + + + +reg_t +kMemoryInfo(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + switch (argv[0].offset) { + case 0: /* Total free heap memory */ + case 1: /* Largest heap block available */ + case 2: /* Largest available hunk memory block */ + case 3: /* Total amount of hunk memory */ + case 4: /* Amount of free DOS paragraphs- SCI01 */ + return make_reg(0, 0x7fff); /* Must not be 0xffff, or some memory calculations will overflow */ + + default: SCIkwarn(SCIkWARNING, "Unknown MemoryInfo operation: %04x\n", argv[0].offset); + } + return NULL_REG; +} + + +reg_t +k_Unknown(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + if (funct_nr >= SCI_MAPPED_UNKNOWN_KFUNCTIONS_NR) { + SCIkwarn(SCIkSTUB, "Unhandled Unknown function %04x\n", funct_nr); + return NULL_REG; + } else switch(kfunct_mappers[funct_nr].type) { + + case KF_NEW: + return kfunct_mappers[funct_nr].new.fun(s, funct_nr, argc, argv); + + case KF_NONE: + default: + SCIkwarn(SCIkSTUB, "Unhandled Unknown function %04x\n", funct_nr); + return NULL_REG; + } +} + + +reg_t +kFlushResources(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + run_gc(s); + SCIkdebug(SCIkROOM, "Entering room number %d\n", UKPV(0)); + return s->r_acc; +} + +reg_t +kSetDebug(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + sciprintf("Debug mode activated\n"); + + script_debug_flag = 1; /* Enter debug mode */ + _debug_seeking = _debug_step_running = 0; + return s->r_acc; +} + +#define _K_NEW_GETTIME_TICKS 0 +#define _K_NEW_GETTIME_TIME_12HOUR 1 +#define _K_NEW_GETTIME_TIME_24HOUR 2 +#define _K_NEW_GETTIME_DATE 3 + +reg_t +kGetTime(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + struct tm* loc_time; + GTimeVal time_prec; + time_t the_time; + int retval = 0; /* Avoid spurious warning */ + +#if 0 + /* Reset optimization flags: If this function is called, + ** the game may be waiting for a timeout */ + s->kernel_opt_flags &= ~(KERNEL_OPT_FLAG_GOT_EVENT + | KERNEL_OPT_FLAG_GOT_2NDEVENT); +#endif + +#ifdef _WIN32 + if (TIMERR_NOERROR != timeBeginPeriod(1)) + { + fprintf(stderr, "timeBeginPeriod(1) failed in kGetTime!\n"); + } +#endif /* _WIN32 */ + + the_time = time(NULL); + loc_time = localtime(&the_time); + +#ifdef _WIN32 + if (TIMERR_NOERROR != timeEndPeriod(1)) + { + fprintf(stderr, "timeEndPeriod(1) failed in kGetTime!\n"); + } +#endif /* _WIN32 */ + + if (s->versiontm_sec + loc_time->tm_min * 60 + (loc_time->tm_hour % 12) * 3600; + SCIkdebug(SCIkTIME, "GetTime(timeofday) returns %d\n", retval); + } else { /* Get time since game started */ + sci_get_current_time (&time_prec); + retval = ((time_prec.tv_usec - s->game_start_time.tv_usec) * 60 / 1000000) + + (time_prec.tv_sec - s->game_start_time.tv_sec) * 60; + SCIkdebug(SCIkTIME, "GetTime(elapsed) returns %d\n", retval); + } + } else { + int mode = UKPV_OR_ALT(0, 0); /* The same strange method is still used for distinguishing + mode 0 and the others. We assume that this is safe, though */ + switch (mode) { + case _K_NEW_GETTIME_TICKS : { + sci_get_current_time (&time_prec); + retval = ((time_prec.tv_usec - s->game_start_time.tv_usec) * 60 / 1000000) + + (time_prec.tv_sec - s->game_start_time.tv_sec) * 60; + SCIkdebug(SCIkTIME, "GetTime(elapsed) returns %d\n", retval); + break; + } + case _K_NEW_GETTIME_TIME_12HOUR : { + loc_time->tm_hour %= 12; + retval=(loc_time->tm_min<<6)|(loc_time->tm_hour<<12)|(loc_time->tm_sec); + SCIkdebug(SCIkTIME, "GetTime(12h) returns %d\n", retval); + break; + } + case _K_NEW_GETTIME_TIME_24HOUR : { + retval=(loc_time->tm_min<<5)|(loc_time->tm_sec>>1)|(loc_time->tm_hour<<11); + SCIkdebug(SCIkTIME, "GetTime(24h) returns %d\n", retval); + break; + } + case _K_NEW_GETTIME_DATE : { + retval=(loc_time->tm_mon<<5)|loc_time->tm_mday|(loc_time->tm_year<<9); + SCIkdebug(SCIkTIME, "GetTime(date) returns %d\n", retval); + break; + } + default: { + SCIkdebug(SCIkWARNING, "Attempt to use unknown GetTime mode %d\n", mode); + break; + } + } + } + + return make_reg(0, retval); +} + +#define K_MEMORY_ALLOCATE_CRITICAL 1 +#define K_MEMORY_ALLOCATE_NONCRITICAL 2 +#define K_MEMORY_FREE 3 +#define K_MEMORY_MEMCPY 4 +#define K_MEMORY_PEEK 5 +#define K_MEMORY_POKE 6 + +reg_t +kMemory(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + + switch (UKPV(0)) { + + case K_MEMORY_ALLOCATE_CRITICAL : + + if (!sm_alloc_dynmem(&s->seg_manager, UKPV(1), + "kMemory() critical", &s->r_acc)) { + SCIkwarn(SCIkERROR, "Critical heap allocation failed\n"); + script_error_flag = script_debug_flag = 1; + } + + return s->r_acc; + break; + + case K_MEMORY_ALLOCATE_NONCRITICAL : + + sm_alloc_dynmem(&s->seg_manager, UKPV(1), + "kMemory() non-critical", &s->r_acc); + break; + + case K_MEMORY_FREE : + + if (sm_free_dynmem(&s->seg_manager, argv[1])) { + SCIkwarn(SCIkERROR, "Attempt to kMemory::free() non-dynmem pointer "PREG"!\n", + PRINT_REG(argv[1])); + } + break; + + case K_MEMORY_MEMCPY : { + int size = UKPV(3); + byte *dest = kernel_dereference_bulk_pointer(s, argv[1], size); + byte *src = kernel_dereference_bulk_pointer(s, argv[2], size); + + if (dest && src) + memcpy(dest, src, size); + else { + SCIkdebug(SCIkWARNING, "Warning: Could not execute kMemory:memcpy of %d bytes:\n", size); + if (!dest) { + SCIkdebug(SCIkWARNING, " dest ptr ("PREG") invalid/memory region too small\n", PRINT_REG(argv[1])); + } + if (!src) { + SCIkdebug(SCIkWARNING, " src ptr ("PREG") invalid/memory region too small\n", PRINT_REG(argv[2])); + } + } + + break; + } + + case K_MEMORY_PEEK : { + byte *ref = kernel_dereference_bulk_pointer(s, argv[1], 2); + + if (!ref) { + SCIkdebug(SCIkERROR, "Attempt to poke invalid memory at "PREG"!\n", + PRINT_REG(argv[1])); + return s->r_acc; + } + if (s->seg_manager.heap[argv[1].segment]->type == MEM_OBJ_LOCALS) + return *((reg_t *) ref); else + return make_reg(0, getInt16(ref)); + } + break; + + case K_MEMORY_POKE : { + byte *ref = kernel_dereference_bulk_pointer(s, argv[1], 2); + + if (!ref) { + SCIkdebug(SCIkERROR, "Attempt to poke invalid memory at "PREG"!\n", + PRINT_REG(argv[1])); + return s->r_acc; + } + + if (s->seg_manager.heap[argv[1].segment]->type == MEM_OBJ_LOCALS) + *((reg_t *) ref) = argv[2]; else + { + if (argv[2].segment) { + SCIkdebug(SCIkERROR, "Attempt to poke memory reference "PREG" to "PREG"!\n", + PRINT_REG(argv[2]), PRINT_REG(argv[1])); + return s->r_acc; + putInt16(ref, argv[2].offset); + } + + } + return s->r_acc; + break; + + } + } + + return s->r_acc; +} + +reg_t +kstub(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int i; + + SCIkwarn(SCIkWARNING, "Unimplemented syscall: %s[%x](", + s->kernel_names[funct_nr], funct_nr); + + for (i = 0; i < argc; i++) { + sciprintf(PREG, PRINT_REG(argv[i])); + if (i+1 < argc) sciprintf(", "); + } + sciprintf(")\n"); + return NULL_REG; +} + + +reg_t +kNOP(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char *problem = (char*)(s->kfunct_table[funct_nr].orig_name ? + "unmapped" : "NOP"); + + SCIkwarn(SCIkWARNING, "Warning: Kernel function 0x%02x invoked: %s", funct_nr, problem); + + if (s->kfunct_table[funct_nr].orig_name && + strcmp(s->kfunct_table[funct_nr].orig_name, SCRIPT_UNKNOWN_FUNCTION_STRING)) + { + sciprintf(" (but its name is known to be %s)", s->kfunct_table[funct_nr].orig_name); + } + + sciprintf("\n"); + return NULL_REG; +} + + + +void +kernel_compile_signature(const char **s) +{ + const char *src = *s; + char *result; + int ellipsis = 0; + char v; + int index = 0; + + if (!src) + return; /* NULL signature: Nothing to do */ + + result = (char*)sci_malloc(strlen(*s) + 1); + + while (*src) { + char c; + v = 0; + + if (ellipsis) { + sciprintf("INTERNAL ERROR when compiling kernel" + " function signature '%s': non-terminal ellipsis\n", *s, + *src); + exit(1); + } + + do { + char cc; + cc = c = *src++; + if (c >= 'A' || c <= 'Z') + cc = c | KSIG_SPEC_SUM_DONE; + + switch (cc) { + + case KSIG_SPEC_LIST: + v |= KSIG_LIST; + break; + + case KSIG_SPEC_NODE: + v |= KSIG_NODE; + break; + + case KSIG_SPEC_REF: + v |= KSIG_REF; + break; + + case KSIG_SPEC_OBJECT: + v |= KSIG_OBJECT; + break; + + case KSIG_SPEC_ARITHMETIC: + v |= KSIG_ARITHMETIC; + break; + + case KSIG_SPEC_NULL: + v |= KSIG_NULL; + break; + + case KSIG_SPEC_ANY: + v |= KSIG_ANY; + break; + + case KSIG_SPEC_ALLOW_INV: + v |= KSIG_ALLOW_INV; + break; + + case KSIG_SPEC_ELLIPSIS: + v |= KSIG_ELLIPSIS; + ellipsis = 1; + break; + + default: { + sciprintf("INTERNAL ERROR when compiling kernel" + " function signature '%s': (%02x) not understood (aka" + " '%c')\n", + *s, c, c); + exit(1); + } + + } + } while (*src && (*src == KSIG_SPEC_ALLOW_INV || + *src == KSIG_SPEC_ELLIPSIS || + (c < 'a' && c != KSIG_SPEC_ANY))); + + /* To handle sum types */ + result[index++] = v; + } + + result[index] = 0; + *s = result; /* Write back */ +} + +int +script_map_kernel(state_t *s) +{ + int functnr; + int mapped = 0; + int ignored = 0; + int functions_nr = s->kernel_names_nr; + int max_functions_nr + = sci_max_allowed_unknown_kernel_functions[s->resmgr + ->sci_version]; + + if (functions_nr < max_functions_nr) { + sciprintf("Warning: SCI version believed to have %d kernel" + " functions, but only %d reported-- filling up" + " remaining %d\n", + max_functions_nr, functions_nr, + max_functions_nr - functions_nr); + + functions_nr = max_functions_nr; + } + + s->kfunct_table = (kfunct_sig_pair_t*)sci_malloc(sizeof(kfunct_sig_pair_t) * functions_nr); + + + s->kfunct_nr = functions_nr; + + for (functnr = 0; functnr < functions_nr; functnr++) { + int seeker, found = -1; + char *sought_name = NULL; + + if (functnr < s->kernel_names_nr) + sought_name = s->kernel_names[functnr]; + + if (sought_name) + for (seeker = 0; (found == -1) + && kfunct_mappers[seeker].type != KF_TERMINATOR; seeker++) + if (kfunct_mappers[seeker].name + && strcmp(kfunct_mappers[seeker].name, sought_name) == 0) + found = seeker; /* Found a kernel function with the same name! */ + + if (found == -1) { + if (sought_name) + { + sciprintf("Warning: Kernel function %s[%x] unmapped\n", + s->kernel_names[functnr], functnr); + s->kfunct_table[functnr].fun = kNOP; + } else + { + sciprintf("Warning: Flagging kernel function %x as unknown\n", + functnr); + s->kfunct_table[functnr].fun = k_Unknown; + } + + s->kfunct_table[functnr].signature = NULL; + s->kfunct_table[functnr].orig_name = sought_name; + } else switch (kfunct_mappers[found].type) { + + case KF_OLD: + sciprintf("Emulated kernel function found.\nThis shouldn't happen anymore."); + return 1; + + case KF_NONE: + s->kfunct_table[functnr].signature = NULL; + ++ignored; + break; + + case KF_NEW: + s->kfunct_table[functnr] = kfunct_mappers[found].new; + kernel_compile_signature(&(s->kfunct_table[functnr].signature)); + ++mapped; + break; + } + + } /* for all functions requesting to be mapped */ + + sciprintf("Handled %d/%d kernel functions, mapping %d", + mapped+ignored, s->kernel_names_nr, mapped); + if (ignored) + sciprintf(" and ignoring %d", ignored); + sciprintf(".\n"); + return 0; +} + +void +free_kfunct_tables(state_t *s) +{ + sci_free(s->kfunct_table); + s->kfunct_table = NULL; + +} +int +determine_reg_type(state_t *s, reg_t reg, int allow_invalid) +{ + mem_obj_t *mobj; + + if (!reg.segment) { + if (!reg.offset) + return KSIG_ARITHMETIC | KSIG_NULL; + return KSIG_ARITHMETIC; + } + + if ((reg.segment >= s->seg_manager.heap_size) + || !s->seg_manager.heap[reg.segment]) + return 0; /* Invalid */ + + mobj = s->seg_manager.heap[reg.segment]; + + switch (mobj->type) { + + case MEM_OBJ_SCRIPT: + if (reg.offset <= mobj->data.script.buf_size + && reg.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET + && RAW_IS_OBJECT(mobj->data.script.buf + reg.offset)) { + int idx = RAW_GET_CLASS_INDEX(&(mobj->data.script), reg); + if (idx >= 0 && idx < mobj->data.script.objects_nr) + return KSIG_OBJECT; + else + return KSIG_REF; + } else + return KSIG_REF; + + case MEM_OBJ_CLONES: + if (allow_invalid || ENTRY_IS_VALID(&(mobj->data.clones), reg.offset)) + return KSIG_OBJECT; + else + return KSIG_OBJECT | KSIG_INVALID; + + case MEM_OBJ_LOCALS: + if (allow_invalid || reg.offset < mobj->data.locals.nr * sizeof(reg_t)) + return KSIG_REF; + else + return KSIG_REF | KSIG_INVALID; + + case MEM_OBJ_STACK: + if (allow_invalid || reg.offset < mobj->data.stack.nr * sizeof(reg_t)) + return KSIG_REF; + else + return KSIG_REF | KSIG_INVALID; + + case MEM_OBJ_SYS_STRINGS: + if (allow_invalid || (reg.offset < SYS_STRINGS_MAX + && mobj->data.sys_strings.strings[reg.offset].name)) + return KSIG_REF; + else + return KSIG_REF | KSIG_INVALID; + + case MEM_OBJ_LISTS: + if (allow_invalid || ENTRY_IS_VALID(&(mobj->data.lists), reg.offset)) + return KSIG_LIST; + else + return KSIG_LIST | KSIG_INVALID; + + case MEM_OBJ_NODES: + if (allow_invalid || ENTRY_IS_VALID(&(mobj->data.nodes), reg.offset)) + return KSIG_NODE; + else + return KSIG_NODE | KSIG_INVALID; + + case MEM_OBJ_DYNMEM: + if (allow_invalid || reg.offset < mobj->data.dynmem.size) + return KSIG_REF; + else + return KSIG_REF | KSIG_INVALID; + + default: + return 0; + + } +} + +const char * +kernel_argtype_description(int type) +{ + type &= ~KSIG_INVALID; + + return argtype_description[sci_ffs(type)]; +} + +int +kernel_matches_signature(state_t *s, const char *sig, int argc, reg_t *argv) +{ + if (!sig) + return 1; + + while (*sig && argc) { + if ((*sig & KSIG_ANY) != KSIG_ANY) { + int type = determine_reg_type(s, *argv, *sig & KSIG_ALLOW_INV); + + if (!type) { + sciprintf("[KERN] Could not determine type of ref "PREG";" + " failing signature check\n", + PRINT_REG(*argv)); + return 0; + } + + if (type & KSIG_INVALID) { + sciprintf("[KERN] ref "PREG" was determined to be a %s, " + "but the reference itself is invalid\n", + PRINT_REG(*argv), kernel_argtype_description(type)); + return 0; + } + + if (!(type & *sig)) + return 0; + + } + if (!(*sig & KSIG_ELLIPSIS)) + ++sig; + ++argv; + --argc; + } + + if (argc) + return 0; /* Too many arguments */ + else + return (*sig == 0 || (*sig & KSIG_ELLIPSIS)); +} + +static inline void * +_kernel_dereference_pointer(struct _state *s, reg_t pointer, int entries, int align) +{ + int maxsize; + void *retval = sm_dereference(&s->seg_manager, pointer, &maxsize); + + if (pointer.offset & (align-1)) { + SCIkdebug(SCIkERROR, "Unaligned pointer read: "PREG" expected with %d alignment!\n", + PRINT_REG(pointer), align); + return NULL; + } + + if (entries > maxsize) { + SCIkdebug(SCIkERROR, "Trying to dereference pointer "PREG" beyond end of segment!\n", + PRINT_REG(pointer)); + return NULL; + } + return retval; + +} + +byte * +kernel_dereference_bulk_pointer(struct _state *s, reg_t pointer, int entries) +{ + return (byte*)_kernel_dereference_pointer(s, pointer, entries, 1); +} + + +reg_t * +kernel_dereference_reg_pointer(struct _state *s, reg_t pointer, int entries) +{ + return (reg_t*)_kernel_dereference_pointer(s, pointer, entries, sizeof(reg_t)); +} diff --git a/engines/sci/engine/kernel_compat.h b/engines/sci/engine/kernel_compat.h new file mode 100644 index 0000000000..0a3468282a --- /dev/null +++ b/engines/sci/engine/kernel_compat.h @@ -0,0 +1,77 @@ +/*************************************************************************** + kernel_compat.h Copyright (C) 2002 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Kernel compatibility #defines to pre-GLUTTON times */ + +#ifndef _SCI_KERNEL_COMPAT_ +#define _SCI_KERNEL_COMPAT_ + +#ifdef __GNUC__ +#warning "Old kernel compatibility crap" +#endif + +/* Minimal heap position */ +#define HEAP_MIN 800 + +#define GET_HEAP(address) ((((guint16)(address)) < HEAP_MIN)? \ +KERNEL_OOPS("Heap address space violation on read") \ +: getHeapInt16(s->heap, ((guint16)(address)))) +/* Reads a heap value if allowed */ + +#define UGET_HEAP(address) ((((guint16)(address)) < HEAP_MIN)? \ +KERNEL_OOPS("Heap address space violation on read") \ +: getHeapUInt16(s->heap, ((guint16)(address)))) +/* Reads a heap value if allowed */ + +#define PUT_HEAP(address, value) { if (((guint16)(address)) < HEAP_MIN) \ +KERNEL_OOPS("Heap address space violation on write"); \ +else { s->heap[((guint16)(address))] = (value) &0xff; \ + s->heap[((guint16)(address)) + 1] = ((value) >> 8) & 0xff;} \ +if ((address) & 1) \ + sciprintf("Warning: Unaligned write to %04x\n", (address) & 0xffff); } +/* Sets a heap value if allowed */ + +static inline int +getHeapInt16(unsigned char *base, int address) +{ + if (address & 1) + sciprintf("Warning: Unaligned read from %04x\n", (address) & 0xffff); + + return getInt16(base + address); +} + +static inline unsigned int +getHeapUInt16(unsigned char *base, int address) +{ + if (address & 1) + sciprintf("Warning: Unaligned unsigned read from %04x\n", (address) & 0xffff); + + return getUInt16(base + address); +} + + + +#endif /* !_SCI_KERNEL_COMPAT_ */ diff --git a/engines/sci/engine/kernel_types.h b/engines/sci/engine/kernel_types.h new file mode 100644 index 0000000000..0ac56bcf6a --- /dev/null +++ b/engines/sci/engine/kernel_types.h @@ -0,0 +1,96 @@ +/*************************************************************************** + kernel_types.h Copyright (C) 2002 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef _FREESCI_KERNEL_TYPES_H_ +#define _FREESCI_KERNEL_TYPES_H_ + +#include "vm_types.h" + +#define KSIG_TERMINATOR 0 + +/* Uncompiled signatures */ +#define KSIG_SPEC_ARITMETIC 'i' +#define KSIG_SPEC_LIST 'l' +#define KSIG_SPEC_NODE 'n' +#define KSIG_SPEC_OBJECT 'o' +#define KSIG_SPEC_REF 'r' /* Said Specs and strings */ +#define KSIG_SPEC_ARITHMETIC 'i' +#define KSIG_SPEC_NULL 'z' +#define KSIG_SPEC_ANY '.' +#define KSIG_SPEC_ALLOW_INV '!' /* Allow invalid pointers */ +#define KSIG_SPEC_ELLIPSIS '*' /* Arbitrarily more TYPED arguments */ + +#define KSIG_SPEC_SUM_DONE ('a' - 'A') /* Use small letters to indicate end of sum type */ +/* Use capital letters for sum types, e.g. +** "LNoLr" for a function which takes two arguments: +** (1) list, node or object +** (2) list or ref +*/ + +/* Compiled signatures */ +#define KSIG_LIST 0x01 +#define KSIG_NODE 0x02 +#define KSIG_OBJECT 0x04 +#define KSIG_REF 0x08 +#define KSIG_ARITHMETIC 0x10 + +#define KSIG_NULL 0x40 +#define KSIG_ANY 0x5f +#define KSIG_ELLIPSIS 0x80 +#define KSIG_ALLOW_INV 0x20 +#define KSIG_INVALID KSIG_ALLOW_INV + + +int +kernel_matches_signature(state_t *s, const char *sig, int argc, reg_t *argv); +/* Determines whether a list of registers matches a given signature +** Parameters: (state_t *) s: The state to operate on +** (char *) sig: The signature to test against +** (int) argc: Number of arguments to test +** (reg_t *) argv: Argument list +** Returns : (int) 0 iff the signature was not matched +*/ + +int +determine_reg_type(state_t *s, reg_t reg, int allow_invalid); +/* Determines the type of the object indicated by reg +** Parameters: (state_t *) s: The state to operate on +** (reg_t) reg: The register to check +** (int) allow_invalid: Allow invalid pointer values +** Returns : one of KSIG_* below KSIG_NULL. +** KSIG_INVALID set if the type of reg can be determined, but is invalid. +** 0 on error. +*/ + +const char * +kernel_argtype_description(int type); +/* Returns a textual description of the type of an object +** Parameters: (int) type: The type value to describe +** Returns: (const char *) Pointer to a (static) descriptive string +*/ + +#endif /* ! _FREESCI_KERNEL_TYPES_H_ */ diff --git a/engines/sci/engine/kevent.c b/engines/sci/engine/kevent.c new file mode 100644 index 0000000000..9054aded41 --- /dev/null +++ b/engines/sci/engine/kevent.c @@ -0,0 +1,229 @@ +/*************************************************************************** + kevent.c Copyright (C) 1999 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + +#include + +int stop_on_event; + +#define SCI_VARIABLE_GAME_SPEED 3 + +reg_t +kGetEvent(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int mask = UKPV(0); + reg_t obj = argv[1]; + sci_event_t e; + int oldx, oldy; + int modifier_mask = SCI_VERSION_MAJOR(s->version)==0 ? SCI_EVM_ALL + : SCI_EVM_NO_FOOLOCK; + + if (s->kernel_opt_flags & KERNEL_OPT_FLAG_GOT_2NDEVENT) { + /* Penalty time- too many requests to this function without + ** waiting! */ + int delay = s->script_000->locals_block->locals[SCI_VARIABLE_GAME_SPEED].offset; + + gfxop_usleep(s->gfx_state, (1000000 * delay) / 60); + } + + /*If there's a simkey pending, and the game wants a keyboard event, use the + *simkey instead of a normal event*/ + if (_kdebug_cheap_event_hack && (mask & SCI_EVT_KEYBOARD)) { + PUT_SEL32V(obj, type, SCI_EVT_KEYBOARD); /*Keyboard event*/ + PUT_SEL32V(obj, message, _kdebug_cheap_event_hack); + PUT_SEL32V(obj, modifiers, SCI_EVM_NUMLOCK); /*Numlock on*/ + PUT_SEL32V(obj, x, s->gfx_state->pointer_pos.x); + PUT_SEL32V(obj, y, s->gfx_state->pointer_pos.y); + _kdebug_cheap_event_hack = 0; + return make_reg(0, 1); + } + + oldx = s->gfx_state->pointer_pos.x; + oldy = s->gfx_state->pointer_pos.y; + e = gfxop_get_event(s->gfx_state, mask); + + s->parser_event = NULL_REG; /* Invalidate parser event */ + + PUT_SEL32V(obj, x, s->gfx_state->pointer_pos.x); + PUT_SEL32V(obj, y, s->gfx_state->pointer_pos.y); + + /* gfxop_set_pointer_position(s->gfx_state, gfx_point(s->gfx_state->pointer_pos.x, s->gfx_state->pointer_pos.y)); */ + + + if (e.type) + s->kernel_opt_flags &= ~(KERNEL_OPT_FLAG_GOT_EVENT + | KERNEL_OPT_FLAG_GOT_2NDEVENT); + else { + if (s->kernel_opt_flags & KERNEL_OPT_FLAG_GOT_EVENT) + s->kernel_opt_flags |= KERNEL_OPT_FLAG_GOT_2NDEVENT; + else + s->kernel_opt_flags |= KERNEL_OPT_FLAG_GOT_EVENT; + } + + switch(e.type) + { + case SCI_EVT_QUIT: + quit_vm(); + break; + + case SCI_EVT_KEYBOARD: { + + if ((e.buckybits & SCI_EVM_LSHIFT) && (e.buckybits & SCI_EVM_RSHIFT) + && (e.data == '-')) { + + sciprintf("Debug mode activated\n"); + + script_debug_flag = 1; /* Enter debug mode */ + _debug_seeking = _debug_step_running = 0; + s->onscreen_console = 0; + + } else if ((e.buckybits & SCI_EVM_CTRL) && (e.data == '`')) { + + script_debug_flag = 1; /* Enter debug mode */ + _debug_seeking = _debug_step_running = 0; + s->onscreen_console = 1; + + } else if ((e.buckybits & SCI_EVM_CTRL) && (e.data == '1')) { + + if (s->visual) + s->visual->print(GFXW(s->visual), 0); + + } else { + PUT_SEL32V(obj, type, SCI_EVT_KEYBOARD); /*Keyboard event*/ + s->r_acc=make_reg(0, 1); + PUT_SEL32V(obj, message, e.character); + /* We only care about the translated + ** character */ + PUT_SEL32V(obj, modifiers, e.buckybits&modifier_mask); + + } + } break; + + case SCI_EVT_MOUSE_RELEASE: + case SCI_EVT_MOUSE_PRESS: { + int extra_bits=0; + + if(mask & e.type) { + switch(e.data) { + case 2: extra_bits=SCI_EVM_LSHIFT|SCI_EVM_RSHIFT; break; + case 3: extra_bits=SCI_EVM_CTRL; + default:break; + } + + PUT_SEL32V(obj, type, e.type); + PUT_SEL32V(obj, message, 1); + PUT_SEL32V(obj, modifiers, (e.buckybits|extra_bits)&modifier_mask); + s->r_acc = make_reg(0, 1); + } + } break; + + default: { + s->r_acc = NULL_REG; /* Unknown or no event */ + } + } + + if ((s->r_acc.offset) && (stop_on_event)) { + stop_on_event = 0; + script_debug_flag = 1; + } + + return s->r_acc; +} + +reg_t +kMapKeyToDir(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t obj = argv[0]; + + if (GET_SEL32V(obj, type) == SCI_EVT_KEYBOARD) { /* Keyboard */ + int mover = -1; + switch (GET_SEL32V(obj, message)) { + case SCI_K_HOME: mover = 8; break; + case SCI_K_UP: mover = 1; break; + case SCI_K_PGUP: mover = 2; break; + case SCI_K_LEFT: mover = 7; break; + case SCI_K_CENTER: + case 76: mover = 0; break; + case SCI_K_RIGHT: mover = 3; break; + case SCI_K_END: mover = 6; break; + case SCI_K_DOWN: mover = 5; break; + case SCI_K_PGDOWN: mover = 4; break; + default: break; + } + + if (mover >= 0) { + PUT_SEL32V(obj, type, SCI_EVT_JOYSTICK); + PUT_SEL32V(obj, message, mover); + return make_reg(0, 1); + } else return NULL_REG; + } + + return s->r_acc; +} + + +reg_t +kGlobalToLocal(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t obj = argc ? argv[0] : NULL_REG; /* Can this really happen? Lars */ + + if (obj.segment) { + int x = GET_SEL32V(obj, x); + int y = GET_SEL32V(obj, y); + + PUT_SEL32V(obj, x, x - s->port->zone.x); + PUT_SEL32V(obj, y, y - s->port->zone.y); + } + + return s->r_acc; + +} + + +reg_t +kLocalToGlobal(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t obj = argc ? argv[0] : NULL_REG; /* Can this really happen? Lars */ + + if (obj.segment) { + int x = GET_SEL32V(obj, x); + int y = GET_SEL32V(obj, y); + + PUT_SEL32V(obj, x, x + s->port->zone.x); + PUT_SEL32V(obj, y, y + s->port->zone.y); + } + + return s->r_acc; +} + +reg_t /* Not implemented */ +kJoystick(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + SCIkdebug(SCIkSTUB, "Unimplemented syscall 'Joystick()'\n", funct_nr); + return NULL_REG; +} + + diff --git a/engines/sci/engine/kfile.c b/engines/sci/engine/kfile.c new file mode 100644 index 0000000000..c4f6c63ac2 --- /dev/null +++ b/engines/sci/engine/kfile.c @@ -0,0 +1,1174 @@ +/*************************************************************************** + Kfile.c Copyright (C) 1999 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + +#include + +#ifdef _WIN32 +# ifndef PATH_MAX +# define PATH_MAX 255 +# endif +# define WIN32_LEAN_AND_MEAN +# include +# include +# include +# define stat _stat +#elif defined (_DREAMCAST) +# include +#endif + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +static int _savegame_indices_nr = -1; /* means 'uninitialized' */ + +static struct _savegame_index_struct { + int id; + long timestamp; +} _savegame_indices[MAX_SAVEGAME_NR]; + +/* This assumes modern stream implementations. It may break on DOS. */ + + +/* Attempts to mirror a file by copying it from the resource firectory +** to the working directory. Returns NULL if the file didn't exist. +** Otherwise, the new file is then opened for reading or writing. +*/ +static FILE * +f_open_mirrored(state_t *s, char *fname) +{ + int fd; + char *buf = NULL; + int fsize; + + chdir(s->resource_dir); + fd = sci_open(fname, O_RDONLY | O_BINARY); + if (!IS_VALID_FD(fd)) { + chdir(s->work_dir); + return NULL; + } + + fsize = sci_fd_size(fd); + if (fsize > 0) { + buf = (char*)sci_malloc(fsize); + read(fd, buf, fsize); + } + + close(fd); + + chdir(s->work_dir); + + /* Visual C++ doesn't allow to specify O_BINARY with creat() */ +#ifdef _MSC_VER + fd = _open(fname, O_CREAT | O_BINARY | O_RDWR, S_IREAD | S_IWRITE); +#else + fd = creat(fname, 0600); +#endif + + if (!IS_VALID_FD(fd) && buf) { + free(buf); + sciprintf("kfile.c: f_open_mirrored(): Warning: Could not create '%s' in '%s' (%d bytes to copy)\n", + fname, s->work_dir, fsize); + return NULL; + } + + if (fsize) { + int ret; + ret = write(fd, buf, fsize); + if (ret < fsize) { + sciprintf("kfile.c: f_open_mirrored(): Warning: Could not write all %ld bytes to '%s' in '%s' (only wrote %ld)\n", + (long)fsize, fname, s->work_dir, ret); + } + + free(buf); + } + + close(fd); + + return sci_fopen(fname, "r" FO_BINARY "+"); +} + + +#define _K_FILE_MODE_OPEN_OR_CREATE 0 +#define _K_FILE_MODE_OPEN_OR_FAIL 1 +#define _K_FILE_MODE_CREATE 2 + + +void +file_open(state_t *s, char *filename, int mode) +{ + int retval = 1; /* Ignore file_handles[0] */ + FILE *file = NULL; + + SCIkdebug(SCIkFILE, "Opening file %s with mode %d\n", filename, mode); + if ((mode == _K_FILE_MODE_OPEN_OR_FAIL) || (mode == _K_FILE_MODE_OPEN_OR_CREATE)) { + file = sci_fopen(filename, "r" FO_BINARY "+"); /* Attempt to open existing file */ + SCIkdebug(SCIkFILE, "Opening file %s with mode %d\n", filename, mode); + if (!file) { + SCIkdebug(SCIkFILE, "Failed. Attempting to copy from resource dir...\n"); + file = f_open_mirrored(s, filename); + if (file) + SCIkdebug(SCIkFILE, "Success!\n"); + else + SCIkdebug(SCIkFILE, "Not found.\n"); + } + } + + if ((!file) && ((mode == _K_FILE_MODE_OPEN_OR_CREATE) || (mode == _K_FILE_MODE_CREATE))) { + file = sci_fopen(filename, "w" FO_BINARY "+"); /* Attempt to create file */ + SCIkdebug(SCIkFILE, "Creating file %s with mode %d\n", filename, mode); + } + if (!file) { /* Failed */ + SCIkdebug(SCIkFILE, "file_open() failed\n"); + s->r_acc = make_reg(0, 0xffff); + return; + } + + while (s->file_handles[retval] && (retval < s->file_handles_nr)) + retval++; + + if (retval == s->file_handles_nr) /* Hit size limit => Allocate more space */ + s->file_handles = (FILE**)sci_realloc(s->file_handles, sizeof(FILE *) * ++(s->file_handles_nr)); + + s->file_handles[retval] = file; + + s->r_acc = make_reg(0, retval); +} + +reg_t +kFOpen(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char *name = kernel_dereference_char_pointer(s, argv[0], 0); + int mode = UKPV(1); + + file_open(s, name, mode); + return s->r_acc; +} + +void file_close(state_t *s, int handle) +{ + SCIkdebug(SCIkFILE, "Closing file %d\n", handle); + + if (handle == 0) { + SCIkwarn(SCIkERROR, "Attempt to close file handle 0\n"); + return; + } + + if ((handle >= s->file_handles_nr) || (s->file_handles[handle] == NULL)) { + SCIkwarn(SCIkERROR, "Attempt to close invalid/unused file handle %d\n", handle); + return; + } + + fclose(s->file_handles[handle]); + + s->file_handles[handle] = NULL; +} + +reg_t +kFClose(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + file_close(s, UKPV(0)); + return s->r_acc; +} + +void fputs_wrapper(state_t *s, int handle, int size, char *data) +{ + SCIkdebug(SCIkFILE, "FPuts'ing \"%s\" to handle %d\n", data, handle); + + if (handle == 0) { + SCIkwarn(SCIkERROR, "Attempt to write to file handle 0\n"); + return; + } + + if ((handle >= s->file_handles_nr) || (s->file_handles[handle] == NULL)) { + SCIkwarn(SCIkERROR, "Attempt to write to invalid/unused file handle %d\n", handle); + return; + } + + fwrite(data, 1, size, s->file_handles[handle]); +} + +void fwrite_wrapper(state_t *s, int handle, char *data, int length) +{ + SCIkdebug(SCIkFILE, "fwrite()'ing \"%s\" to handle %d\n", data, handle); + + if (handle == 0) { + SCIkwarn(SCIkERROR, "Attempt to write to file handle 0\n"); + return; + } + + if ((handle >= s->file_handles_nr) || (s->file_handles[handle] == NULL)) { + SCIkwarn(SCIkERROR, "Attempt to write to invalid/unused file handle %d\n", handle); + return; + } + + fwrite(data, 1, length, s->file_handles[handle]); +} + + +reg_t kFPuts(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int handle = UKPV(0); + char *data = kernel_dereference_char_pointer(s, argv[1], 0); + + fputs_wrapper(s, handle, strlen(data), data); + return s->r_acc; +} + +static void +fgets_wrapper(state_t *s, char *dest, int maxsize, int handle) +{ + SCIkdebug(SCIkFILE, "FGets'ing %d bytes from handle %d\n", maxsize, handle); + + + if (handle == 0) { + SCIkwarn(SCIkERROR, "Attempt to read from file handle 0\n"); + return; + } + + if ((handle >= s->file_handles_nr) || (s->file_handles[handle] == NULL)) { + SCIkwarn(SCIkERROR, "Attempt to read from invalid/unused file handle %d\n", handle); + return; + } + + fgets(dest, maxsize, s->file_handles[handle]); + + SCIkdebug(SCIkFILE, "FGets'ed \"%s\"\n", dest); +} + + +static void +fread_wrapper(state_t *s, char *dest, int bytes, int handle) +{ + SCIkdebug(SCIkFILE, "fread()'ing %d bytes from handle %d\n", bytes, handle); + + if (handle == 0) { + SCIkwarn(SCIkERROR, "Attempt to read from file handle 0\n"); + return; + } + + if ((handle >= s->file_handles_nr) || (s->file_handles[handle] == NULL)) { + SCIkwarn(SCIkERROR, "Attempt to read from invalid/unused file handle %d\n", handle); + return; + } + + s->r_acc=make_reg(0,fread(dest, 1, bytes, s->file_handles[handle])); +} + + +static void +fseek_wrapper(state_t *s, int handle, int offset, int whence) +{ + + if (handle == 0) { + SCIkwarn(SCIkERROR, "Attempt seek on file handle 0\n"); + return; + } + + if ((handle >= s->file_handles_nr) || (s->file_handles[handle] == NULL)) { + SCIkwarn(SCIkERROR, "Attempt seek on invalid/unused file handle %d\n", handle); + return; + } + + s->r_acc=make_reg(0, fseek(s->file_handles[handle], offset, whence)); +} + + +static char * +_chdir_savedir(state_t *s) +{ + char *cwd = sci_getcwd(); + char *save_dir = kernel_dereference_char_pointer(s, + make_reg(s->sys_strings_segment, SYS_STRING_SAVEDIR), 0); + + if (chdir(save_dir) && sci_mkpath(save_dir)) { + + sciprintf(__FILE__": Can't chdir to savegame dir '%s' or " + "create it\n", save_dir); + + sci_free(cwd); + return NULL; + } + + if (!cwd) + cwd = strdup(s->work_dir); + + return cwd; /* Potentially try again */ +} + +static void +_chdir_restoredir(char *dir) +{ + if (chdir(dir)) { + sciprintf(__FILE__": Can't seem to return to previous homedir '%s'\n", + dir); + } + free(dir); +} + +#define TEST_DIR_OR_QUIT(dir) if (!dir) { return NULL_REG; } + +reg_t +kFGets(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char *dest = kernel_dereference_char_pointer(s, argv[0], 0); + int maxsize = UKPV(1); + int handle = UKPV(2); + + fgets_wrapper(s, dest, maxsize, handle); + return argv[0]; +} + + +/* kGetCWD(address): +** Writes the cwd to the supplied address and returns the address in acc. +*/ +reg_t +kGetCWD(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char *wd = sci_getcwd(); + char *targetaddr = kernel_dereference_char_pointer(s, argv[0], 0); + + strncpy(targetaddr, wd, MAX_SAVE_DIR_SIZE - 1); + targetaddr[MAX_SAVE_DIR_SIZE - 1] = 0; /* Terminate */ + + SCIkdebug(SCIkFILE, "Copying cwd='%s'(%d chars) to %p", + wd, strlen(wd), targetaddr); + + free(wd); + return argv[0]; +} + +/* Returns a dynamically allocated pointer to the name of the requested save dir */ +char * +_k_get_savedir_name(int nr) +{ + char suffices[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + char *savedir_name = (char*)sci_malloc(strlen(FREESCI_SAVEDIR_PREFIX) + 2); + assert(nr >= 0); + assert(nr < MAX_SAVEGAME_NR); + strcpy(savedir_name, FREESCI_SAVEDIR_PREFIX); + savedir_name[strlen(FREESCI_SAVEDIR_PREFIX)] = suffices[nr]; + savedir_name[strlen(FREESCI_SAVEDIR_PREFIX) + 1] = 0; + + return savedir_name; +} + +void +delete_savegame(state_t *s, int savedir_nr) +{ + char *workdir = _chdir_savedir(s); + char *savedir = _k_get_savedir_name(savedir_nr); + char buffer[256]; + + sciprintf("Deleting savegame '%s'\n", savedir); + + sprintf(buffer, "%s/%s.id", savedir, s->game_name); + sci_unlink(buffer); + + sprintf(buffer, "%s/state", savedir); + sci_unlink(buffer); + + sci_rmdir(savedir); + + free(savedir); + _chdir_restoredir(workdir); +} + +#define K_DEVICE_INFO_GET_DEVICE 0 +#define K_DEVICE_INFO_GET_CURRENT_DEVICE 1 +#define K_DEVICE_INFO_PATHS_EQUAL 2 +#define K_DEVICE_INFO_IS_FLOPPY 3 +#define K_DEVICE_INFO_GET_SAVECAT_NAME 7 +#define K_DEVICE_INFO_GET_SAVEFILE_NAME 8 + +#ifdef _WIN32 + +reg_t +kDeviceInfo_Win32(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char dir_buffer [MAX_PATH], dir_buffer2 [MAX_PATH]; + int mode = UKPV(0); + + + switch(mode) { + + case K_DEVICE_INFO_GET_DEVICE: { + char *input_s = (char*)kernel_dereference_bulk_pointer(s, argv[1], 0); + char *output_s = (char*)kernel_dereference_bulk_pointer(s, argv[2], 0); + + GetFullPathName (input_s, sizeof (dir_buffer)-1, dir_buffer, NULL); + strncpy(output_s, dir_buffer, 2); + output_s [2] = 0; + } + break; + + case K_DEVICE_INFO_GET_CURRENT_DEVICE: { + char *output_s = (char*)kernel_dereference_bulk_pointer(s, argv[1], 0); + + _getcwd (dir_buffer, sizeof (dir_buffer)-1); + strncpy(output_s, dir_buffer, 2); + output_s [2] = 0; + } + break; + + case K_DEVICE_INFO_PATHS_EQUAL: { + char *path1_s = (char*)kernel_dereference_bulk_pointer(s, argv[1], 0); + char *path2_s = (char*)kernel_dereference_bulk_pointer(s, argv[2], 0); + + GetFullPathName (path1_s, sizeof (dir_buffer)-1, dir_buffer, NULL); + GetFullPathName (path2_s, sizeof (dir_buffer2)-1, dir_buffer2, NULL); + +#ifdef _MSC_VER + return make_reg(0, !stricmp (path1_s, path2_s)); +#else + return make_reg(0, !strcasecmp (path1_s, path2_s)); +#endif + } + break; + + case K_DEVICE_INFO_IS_FLOPPY: { + char *input_s = (char*)kernel_dereference_bulk_pointer(s, argv[1], 0); + + GetFullPathName (input_s, sizeof (dir_buffer)-1, dir_buffer, NULL); + dir_buffer [3] = 0; /* leave X:\ */ + + return make_reg(0, GetDriveType (dir_buffer) == DRIVE_REMOVABLE); + } + break; + +/* SCI uses these in a less-than-portable way to delete savegames. +** Read http://www-plan.cs.colorado.edu/creichen/freesci-logs/2005.10/log20051019.html +** for more information on our workaround for this. +*/ + case K_DEVICE_INFO_GET_SAVECAT_NAME: { + char *output_buffer = kernel_dereference_char_pointer(s, argv[1], 0); + char *game_prefix = kernel_dereference_char_pointer(s, argv[2], 0); + + sprintf(output_buffer, "%s/__throwaway", s->work_dir); + } + + break; + case K_DEVICE_INFO_GET_SAVEFILE_NAME: { + char *output_buffer = kernel_dereference_char_pointer(s, argv[1], 0); + char *game_prefix = kernel_dereference_char_pointer(s, argv[2], 0); + int savegame_id = UKPV(3); + sprintf(output_buffer, "%s/__throwaway", s->work_dir); + delete_savegame(s, savegame_id); + } + break; + default: { + SCIkwarn(SCIkERROR, "Unknown DeviceInfo() sub-command: %d\n", mode); + } + } + return s->r_acc; +} + +#else /* !_WIN32 */ + +reg_t +kDeviceInfo_Unix(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int mode = UKPV(0); + + switch(mode) { + + case K_DEVICE_INFO_GET_DEVICE: { + char *output_s = kernel_dereference_char_pointer(s, argv[2], 0); + + strcpy(output_s, "/"); + } + break; + + case K_DEVICE_INFO_GET_CURRENT_DEVICE: { + char *output_s = kernel_dereference_char_pointer(s, argv[1], 0); + + strcpy(output_s, "/"); + } + break; + + case K_DEVICE_INFO_PATHS_EQUAL: { + char *path1_s = kernel_dereference_char_pointer(s, argv[1], 0); + char *path2_s = kernel_dereference_char_pointer(s, argv[2], 0); + +#ifndef HAVE_FNMATCH_H +#ifndef _DOS +# warning "File matches will be unprecise!" +#endif + return make_reg(0, !strcmp(path1_s, path2_s)); +#else + return make_reg(0, fnmatch(path1_s, path2_s, FNM_PATHNAME) /* POSIX.2 */); +#endif + } + break; + + case K_DEVICE_INFO_IS_FLOPPY: { + + return NULL_REG; /* Never */ + } + break; + +/* SCI uses these in a less-than-portable way to delete savegames. +** Read http://www-plan.cs.colorado.edu/creichen/freesci-logs/2005.10/log20051019.html +** for more information on our workaround for this. +*/ + case K_DEVICE_INFO_GET_SAVECAT_NAME: { + char *output_buffer = kernel_dereference_char_pointer(s, argv[1], 0); +/* char *game_prefix = kernel_dereference_char_pointer(s, argv[2], 0);*/ + + sprintf(output_buffer, "%s/__throwaway", s->work_dir); + } + + break; + case K_DEVICE_INFO_GET_SAVEFILE_NAME: { + char *output_buffer = kernel_dereference_char_pointer(s, argv[1], 0); +/* char *game_prefix = kernel_dereference_char_pointer(s, argv[2], 0);*/ + int savegame_id = UKPV(3); + sprintf(output_buffer, "%s/__throwaway", s->work_dir); + delete_savegame(s, savegame_id); + } + break; + default: { + SCIkwarn(SCIkERROR, "Unknown DeviceInfo() sub-command: %d\n", mode); + } + } + + return s->r_acc; +} + +#endif /* !_WIN32 */ + + +reg_t +kGetSaveDir(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + return make_reg(s->sys_strings_segment, SYS_STRING_SAVEDIR); +} + + +reg_t +kCheckFreeSpace(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char *path = kernel_dereference_char_pointer(s, argv[0], 0); + char *testpath = (char*)sci_malloc(strlen(path) + 15); + char buf[1024]; + int i; + int fd; + int failed = 0; + int pathlen; + + strcpy(testpath, path); + strcat(testpath, "freesci.foo"); + pathlen = strlen(testpath); + + while (IS_VALID_FD(fd = open(testpath, O_RDONLY))) { + close(fd); + if (testpath[pathlen - 2] == 'z') { /* Failed. */ + SCIkwarn(SCIkWARNING, "Failed to find non-existing file for free space test\n"); + free(testpath); + return NULL_REG; + } + + /* If this file couldn't be created, try freesci.fop, freesci.foq etc., + ** then freesci.fpa, freesci.fpb. Stop at freesci.fza. + ** Yes, this is extremely arbitrary and very strange. + */ + if (testpath[pathlen - 1] == 'z') { + testpath[pathlen - 1] = 'a'; + ++testpath[pathlen - 2]; + } + else + ++testpath[pathlen - 1]; + } + + fd = creat(testpath, 0600); + + if (!IS_VALID_FD(fd)) { + SCIkwarn(SCIkWARNING,"Could not test for disk space: %s\n", strerror(errno)); + SCIkwarn(SCIkWARNING,"Test path was '%s'\n", testpath); + free(testpath); + return NULL_REG; + } + + memset(buf, 0, sizeof(buf)); + for (i = 0; i < 1024; i++) /* Check for 1 MB */ + if (write(fd, buf, 1024) < 1024) + failed = 1; + + close(fd); + + remove(testpath); + + sci_free(testpath); + + return make_reg(0, !failed); +} + + + +int +_k_check_file(char *filename, int minfilesize) + /* Returns 0 if the file exists and is big enough */ +{ + return (sci_file_size(filename) < minfilesize); +} + +int +_k_find_savegame_by_name(char *game_id_file, char *name) +{ + int savedir_nr = -1; + int i; + char *buf = NULL; + + for (i = 0; i < MAX_SAVEGAME_NR; i++) { + if (!chdir((buf = _k_get_savedir_name(i)))) { + char namebuf[32]; /* Save game name buffer */ + FILE *idfile = sci_fopen(game_id_file, "r"); + + if (idfile) { + fgets(namebuf, 31, idfile); + if (strlen(namebuf) > 0) + if (namebuf[strlen(namebuf) - 1] == '\n') + namebuf[strlen(namebuf) - 1] = 0; /* Remove trailing newlines */ + + if (strcmp(name, namebuf) == 0) { + sciprintf("Save game name matched entry %d\n", i); + savedir_nr = i; + } + + fclose(idfile); + } + + chdir(".."); + } + free(buf); + } + return 0; +} + +#ifdef _DREAMCAST +static long +get_file_mtime(int fd) +{ + /* FIXME (Dreamcast): Not yet implemented */ + return 0; +} + +#else + +#define get_file_mtime(fd) get_file_mtime_Unix(fd) +/* Returns the time of the specified file's last modification +** Parameters: (int) fd: The file descriptor of the file in question +** Returns : (long) An integer value describing the time of the +** file's last modification. +** The only thing that must be ensured is that +** get_file_mtime(f1) > get_file_mtime(f2) +** <=> +** if f1 was modified at a later point in time than the last time +** f2 was modified. +*/ + +static long +get_file_mtime_Unix(int fd) /* returns the */ +{ + struct stat fd_stat; + fstat(fd, &fd_stat); + + return fd_stat.st_ctime; +} +#endif + +static int +_savegame_index_struct_compare(const void *a, const void *b) +{ + return ((struct _savegame_index_struct *)b)->timestamp + - ((struct _savegame_index_struct *)a)->timestamp; +} + +static void +update_savegame_indices(char *gfname) +{ + int i; + + _savegame_indices_nr = 0; + + for (i = 0; i < MAX_SAVEGAME_NR; i++) { + char *dirname = _k_get_savedir_name(i); + int fd; + + if (!chdir(dirname)) { + + if (IS_VALID_FD(fd = sci_open(gfname, O_RDONLY))) { + _savegame_indices[_savegame_indices_nr].id = i; + _savegame_indices[_savegame_indices_nr++].timestamp = get_file_mtime(fd); + close(fd); + } + chdir(".."); + } + + free(dirname); + } + + qsort(_savegame_indices, _savegame_indices_nr, sizeof(struct _savegame_index_struct), + _savegame_index_struct_compare); + +} + +int +test_savegame(state_t *s, char *savegame_id, char *savegame_name, int savegame_name_length) +{ + FILE *f; + char buffer[80]; + int version = -1; + + chdir(savegame_id); + f = fopen("state", "r"); + + if (!f) return 0; + while (!feof(f)) + { + char *seeker; + fgets(buffer, sizeof(buffer), f); + if ((seeker = strstr(buffer, "savegame_version = ")) != NULL) + { + seeker += strlen("savegame_version = "); + version = strtol(seeker, NULL, 10); + break; + } + } + + fclose(f); + return version >= FREESCI_MINIMUM_SAVEGAME_VERSION && + version <= FREESCI_CURRENT_SAVEGAME_VERSION; +} + +reg_t +kCheckSaveGame(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char *game_id = kernel_dereference_char_pointer(s, argv[0], 0); + int savedir_nr = UKPV(1); + char *buf = NULL; + char *workdir = _chdir_savedir(s); + TEST_DIR_OR_QUIT(workdir); + + if (_savegame_indices_nr < 0) { + char *game_id_file_name = (char*)sci_malloc(strlen(game_id) + strlen(FREESCI_ID_SUFFIX) + 1); + + strcpy(game_id_file_name, game_id); + strcat(game_id_file_name, FREESCI_ID_SUFFIX); + SCIkwarn(SCIkWARNING, "Savegame index list not initialized!\n"); + update_savegame_indices(game_id_file_name); + } + + savedir_nr = _savegame_indices[savedir_nr].id; + + + if (savedir_nr > MAX_SAVEGAME_NR-1) { + _chdir_restoredir(workdir); + return NULL_REG; + } + + s->r_acc = make_reg(0, test_savegame(s, (buf = _k_get_savedir_name(savedir_nr)), NULL, 0)); + + _chdir_restoredir(workdir); + free(buf); + + return s->r_acc; +} + + +reg_t +kGetSaveFiles(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char *game_id = kernel_dereference_char_pointer(s, argv[0], 0); + char *nametarget = kernel_dereference_char_pointer(s, argv[1], 0); + reg_t nametarget_base = argv[1]; + reg_t *nameoffsets = kernel_dereference_reg_pointer(s, argv[2], 0); + int gfname_len = strlen(game_id) + strlen(FREESCI_ID_SUFFIX) + 1; + char *gfname = (char*)sci_malloc(gfname_len); + int i; + char *workdir = _chdir_savedir(s); + TEST_DIR_OR_QUIT(workdir); + + strcpy(gfname, game_id); + strcat(gfname, FREESCI_ID_SUFFIX); /* This file is used to identify in-game savegames */ + + update_savegame_indices(gfname); + + s->r_acc = NULL_REG; + + for (i = 0; i < _savegame_indices_nr; i++) { + char *savedir_name = _k_get_savedir_name(_savegame_indices[i].id); + FILE *idfile; + + if (!chdir(savedir_name)) { + + + if ((idfile = sci_fopen(gfname, "r"))) { /* Valid game ID file: Assume valid game */ + char namebuf[SCI_MAX_SAVENAME_LENGTH]; /* Save game name buffer */ + fgets(namebuf, SCI_MAX_SAVENAME_LENGTH-1, idfile); + if (strlen(namebuf) > 0) { + + if (namebuf[strlen(namebuf) - 1] == '\n') + namebuf[strlen(namebuf) - 1] = 0; /* Remove trailing newline */ + + *nameoffsets = s->r_acc; /* Store savegame ID */ + ++s->r_acc.offset; /* Increase number of files found */ + + nameoffsets++; /* Make sure the next ID string address is written to the next pointer */ + strncpy(nametarget, namebuf, SCI_MAX_SAVENAME_LENGTH); /* Copy identifier string */ + *(nametarget + SCI_MAX_SAVENAME_LENGTH - 1) = 0; /* Make sure it's terminated */ + nametarget += SCI_MAX_SAVENAME_LENGTH; /* Increase name offset pointer accordingly */ + nametarget_base.offset += SCI_MAX_SAVENAME_LENGTH; + + fclose(idfile); + } + } + chdir(".."); + } + free(savedir_name); + } + + free(gfname); + *nametarget = 0; /* Terminate list */ + + _chdir_restoredir(workdir); + return s->r_acc; +} + +reg_t +kSaveGame(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char *game_id = (char*)kernel_dereference_bulk_pointer(s, argv[0], 0); + char *savegame_dir; + int savedir_nr = UKPV(1); + int savedir_id; /* Savegame ID, derived from savedir_nr and the savegame ID list */ + char *game_id_file_name = (char*)sci_malloc(strlen(game_id) + strlen(FREESCI_ID_SUFFIX) + 1); + char *game_description = (char*)kernel_dereference_bulk_pointer(s, argv[2], 0); + char *workdir = _chdir_savedir(s); + char *version = argc > 3 ? strdup((char*)kernel_dereference_bulk_pointer(s, argv[3], 0)) : NULL; + TEST_DIR_OR_QUIT(workdir); + + s->game_version = version; + + strcpy(game_id_file_name, game_id); + strcat(game_id_file_name, FREESCI_ID_SUFFIX); + + update_savegame_indices(game_id_file_name); + + if (savedir_nr >= 0 && savedir_nr < _savegame_indices_nr) + /* Overwrite */ + savedir_id = _savegame_indices[savedir_nr].id; + else if (savedir_nr >= 0 && savedir_nr < MAX_SAVEGAME_NR) { + int i = 0; + + savedir_id = 0; + + /* First, look for holes */ + while (i < _savegame_indices_nr) + if (_savegame_indices[i].id == savedir_id) { + ++savedir_id; + i = 0; + } else ++i; + + if (savedir_id >= MAX_SAVEGAME_NR) { + sciprintf("Internal error: Free savegame ID is %d, shouldn't happen!\n", + savedir_id); + return NULL_REG; + } + + /* This loop terminates when savedir_id is not in [x | ex. n. _savegame_indices[n].id = x] */ + } else { + sciprintf("Savegame ID %d is not allowed!\n", savedir_nr); + return NULL_REG; + } + + savegame_dir = _k_get_savedir_name(savedir_id); + + if (gamestate_save(s, savegame_dir)) { + sciprintf("Saving the game failed.\n"); + s->r_acc = NULL_REG; + } else { + FILE *idfile; + + chdir(savegame_dir); + + if ((idfile = sci_fopen(game_id_file_name, "w"))) { + + fprintf(idfile, game_description); + fclose(idfile); + + } else { + sciprintf("Creating the game ID file failed.\n"); + sciprintf("You can still restore from inside the debugger with \"restore_game %s\"\n", savegame_dir); + s->r_acc = NULL_REG; + } + + chdir (".."); + s->r_acc = make_reg(0, 1); + } + free(game_id_file_name); + _chdir_restoredir(workdir); + + free(s->game_version); + s->game_version = NULL; + return s->r_acc; +} + + +reg_t +kRestoreGame(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char *game_id = (char*)kernel_dereference_bulk_pointer(s, argv[0], 0); + int savedir_nr = UKPV(1); + char *workdir = _chdir_savedir(s); + TEST_DIR_OR_QUIT(workdir); + + if (_savegame_indices_nr < 0) { + char *game_id_file_name = (char*)sci_malloc(strlen(game_id) + strlen(FREESCI_ID_SUFFIX) + 1); + + strcpy(game_id_file_name, game_id); + strcat(game_id_file_name, FREESCI_ID_SUFFIX); + SCIkwarn(SCIkWARNING, "Savegame index list not initialized!\n"); + update_savegame_indices(game_id_file_name); + } + + savedir_nr = _savegame_indices[savedir_nr].id; + + + if (savedir_nr > -1) { + char *savedir_name = _k_get_savedir_name(savedir_nr); + state_t *newstate = gamestate_restore(s, savedir_name); + + free(savedir_name); + + if (newstate) { + + s->successor = newstate; + script_abort_flag = SCRIPT_ABORT_WITH_REPLAY; /* Abort current game */ + s->execution_stack_pos = s->execution_stack_base; + + } else { + s->r_acc = make_reg(0, 1); + sciprintf("Restoring failed (game_id = '%s').\n", game_id); + } + + } else { + s->r_acc = make_reg(0, 1); + sciprintf("Savegame #%d not found!\n", savedir_nr); + } + + _chdir_restoredir(workdir); + return s->r_acc; +} + + +reg_t +kValidPath(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char *pathname = kernel_dereference_char_pointer(s, argv[0], 0); + char cpath[PATH_MAX + 1]; + getcwd(cpath, PATH_MAX + 1); + + s->r_acc = make_reg(0, !chdir(pathname)); /* Try to go there. If it works, return 1, 0 otherwise. */ + + chdir(cpath); + + return s->r_acc; +} + +#define K_FILEIO_OPEN 0 +#define K_FILEIO_CLOSE 1 +#define K_FILEIO_READ_RAW 2 +#define K_FILEIO_WRITE_RAW 3 +#define K_FILEIO_UNLINK 4 +#define K_FILEIO_READ_STRING 5 +#define K_FILEIO_WRITE_STRING 6 +#define K_FILEIO_SEEK 7 +#define K_FILEIO_FIND_FIRST 8 +#define K_FILEIO_FIND_NEXT 9 +#define K_FILEIO_STAT 10 + + +char * +write_filename_to_mem(state_t *s, reg_t address, char *string) +{ + char *mem = kernel_dereference_char_pointer(s, address, 0); + + if (string) { + memset(mem, 0, 13); + strncpy(mem, string, 12); + } + + return string; +} + +void +next_file(state_t *s) +{ + if (write_filename_to_mem(s, s->dirseeker_outbuffer, + sci_find_next(&(s->dirseeker)))) + s->r_acc = s->dirseeker_outbuffer; + else + s->r_acc = NULL_REG; +} + +void +first_file(state_t *s, const char *dir, char *mask, reg_t buffer) +{ + if (!buffer.segment) { + sciprintf("Warning: first_file(state,\"%s\",\"%s\", 0) invoked!\n", + dir, mask); + s->r_acc = NULL_REG; + return; + } + + if (strcmp(dir, ".")) { + sciprintf("%s L%d: Non-local first_file: Not implemented yet\n", + __FILE__, __LINE__); + s->r_acc = NULL_REG; + return; + } + + /* Get rid of the old find structure */ + if (s->dirseeker_outbuffer.segment) + sci_finish_find(&(s->dirseeker)); + + s->dirseeker_outbuffer = buffer; + + if (write_filename_to_mem(s, s->dirseeker_outbuffer, + sci_find_first(&(s->dirseeker), mask))) + s->r_acc = s->dirseeker_outbuffer; + else + s->r_acc = NULL_REG; +} + +reg_t +kFileIO(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int func_nr = UKPV(0); + + switch (func_nr) { + + case K_FILEIO_OPEN : + { + char *name = kernel_dereference_char_pointer(s, argv[1], 0); + int mode = UKPV(2); + + file_open(s, name, mode); + break; + } + case K_FILEIO_CLOSE : + { + int handle = UKPV(1); + + file_close(s, handle); + break; + } + case K_FILEIO_READ_RAW : + { + int handle = UKPV(1); + char *dest = kernel_dereference_char_pointer(s, argv[2], 0); + int size = UKPV(3); + + fread_wrapper(s, dest, size, handle); + break; + } + case K_FILEIO_WRITE_RAW : + { + int handle = UKPV(1); + char *buf = kernel_dereference_char_pointer(s, argv[2], 0); + int size = UKPV(3); + + fwrite_wrapper(s, handle, buf, size); + break; + } + case K_FILEIO_UNLINK : + { + char *name = kernel_dereference_char_pointer(s, argv[1], 0); + + unlink(name); + break; + } + case K_FILEIO_READ_STRING : + { + char *dest = kernel_dereference_char_pointer(s, argv[1], 0); + int size = UKPV(2); + int handle = UKPV(3); + + fgets_wrapper(s, dest, size, handle); + return argv[1]; + } + case K_FILEIO_WRITE_STRING : + { + int handle = UKPV(1); + int size = UKPV(3); + char *buf = kernel_dereference_char_pointer(s, argv[2], size); + + if (buf) + fputs_wrapper(s, handle, size, buf); + break; + } + case K_FILEIO_SEEK : + { + int handle = UKPV(1); + int offset = UKPV(2); + int whence = UKPV(3); + + fseek_wrapper(s, handle, offset, whence); + break; + } + case K_FILEIO_FIND_FIRST : + { + char *mask = kernel_dereference_char_pointer(s, argv[1], 0); + reg_t buf = argv[2]; + /* int attr = UKPV(3); */ /* We won't use this, Win32 might, though... */ + +#ifndef _WIN32 + if (strcmp(mask, "*.*")==0) strcpy(mask, "*"); /* For UNIX */ +#endif + first_file(s, ".", mask, buf); + + break; + } + case K_FILEIO_FIND_NEXT : + { + next_file(s); + break; + } + case K_FILEIO_STAT : + { + char *name = kernel_dereference_char_pointer(s, argv[1], 0); + s->r_acc=make_reg(0, 1-_k_check_file(name, 0)); + break; + } + default : + SCIkwarn(SCIkERROR, "Unknown FileIO() sub-command: %d\n", func_nr); + } + + return s->r_acc; +} diff --git a/engines/sci/engine/kgraphics.c b/engines/sci/engine/kgraphics.c new file mode 100644 index 0000000000..2f15877ac8 --- /dev/null +++ b/engines/sci/engine/kgraphics.c @@ -0,0 +1,3627 @@ +/*************************************************************************** + kgraphics.c Copyright (C) 1999,2000..04 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) [creichen@gmail.com] + +***************************************************************************/ + +#include +#include +#include +#include "sci_graphics.h" +#include + +#undef DEBUG_LSRECT + +/* Graph subfunctions */ +#define K_GRAPH_GET_COLORS_NR 2 +#define K_GRAPH_DRAW_LINE 4 +#define K_GRAPH_SAVE_BOX 7 +#define K_GRAPH_RESTORE_BOX 8 +#define K_GRAPH_FILL_BOX_BACKGROUND 9 +#define K_GRAPH_FILL_BOX_FOREGROUND 10 +#define K_GRAPH_FILL_BOX_ANY 11 +#define K_GRAPH_UPDATE_BOX 12 +#define K_GRAPH_REDRAW_BOX 13 +#define K_GRAPH_ADJUST_PRIORITY 14 + +/* Control types and flags */ +#define K_CONTROL_BUTTON 1 +#define K_CONTROL_TEXT 2 +#define K_CONTROL_EDIT 3 +#define K_CONTROL_ICON 4 +#define K_CONTROL_CONTROL 6 +#define K_CONTROL_CONTROL_ALIAS 7 +#define K_CONTROL_BOX 10 + + +#define ADD_TO_CURRENT_PORT(widget) \ + {if (s->port) \ + s->port->add(GFXWC(s->port), GFXW(widget)); \ + else \ + s->picture_port->add(GFXWC(s->visual), GFXW(widget));} + +#define ADD_TO_CURRENT_PICTURE_PORT(widget) \ + {if (s->port) \ + s->port->add(GFXWC(s->port), GFXW(widget)); \ + else \ + s->picture_port->add(GFXWC(s->picture_port), GFXW(widget));} + +#define ADD_TO_WINDOW_PORT(widget) \ + s->wm_port->add(GFXWC(s->wm_port), GFXW(widget)); + +#define ADD_TO_CURRENT_FG_WIDGETS(widget) \ + ADD_TO_CURRENT_PICTURE_PORT(widget) + +#define ADD_TO_CURRENT_BG_WIDGETS(widget) \ + ADD_TO_CURRENT_PICTURE_PORT(widget) + +#define FULL_REDRAW()\ + if (s->visual) \ + s->visual->draw(GFXW(s->visual), gfxw_point_zero); \ + gfxop_update(s->gfx_state); + +#define FULL_INSPECTION()\ + if (s->visual) \ + s->visual->print(GFXW(s->visual), 0); + + +#define GFX_ASSERT(x) { \ + int val = !!(x); \ + if (val) { \ + if (val == GFX_ERROR) \ + SCIkwarn(SCIkWARNING, "GFX subsystem returned error on \"" #x "\"!\n"); \ + else {\ + SCIkwarn(SCIkERROR, "GFX subsystem fatal error condition on \"" #x "\"!\n"); \ + vm_handle_fatal_error(s, __LINE__, __FILE__); \ + } \ + }\ +} + +#define ASSERT(x) { \ + int val = !!(x); \ + if (!val) { \ + SCIkwarn(SCIkERROR, "Fatal error condition on \"" #x "\"!\n"); \ + BREAKPOINT(); \ + vm_handle_fatal_error(s, __LINE__, __FILE__); \ + } \ +} + + +static inline int +sign_extend_byte(int value) +{ + if (value & 0x80) + return value - 256; + else + return value; +} + + +static void +assert_primary_widget_lists(state_t *s) +{ + if (!s->dyn_views) { + rect_t bounds = s->picture_port->bounds; + + s->dyn_views = gfxw_new_list(bounds, GFXW_LIST_SORTED); + s->dyn_views->flags |= GFXW_FLAG_IMMUNE_TO_SNAPSHOTS; + ADD_TO_CURRENT_PICTURE_PORT(s->dyn_views); + } + + if (!s->drop_views) { + rect_t bounds = s->picture_port->bounds; + + s->drop_views = gfxw_new_list(bounds, GFXW_LIST_SORTED); + s->drop_views->flags |= GFXW_FLAG_IMMUNE_TO_SNAPSHOTS; + ADD_TO_CURRENT_PICTURE_PORT(s->drop_views); + } +} + +static void +reparentize_primary_widget_lists(state_t *s, gfxw_port_t *newport) +{ + if (!newport) + newport = s->picture_port; + + if (s->dyn_views) { + gfxw_remove_widget_from_container(s->dyn_views->parent, GFXW(s->dyn_views)); + + newport->add(GFXWC(newport), GFXW(s->dyn_views)); + } +} + +int +_find_view_priority(state_t *s, int y) +{ + /* if (s->version <= SCI_VERSION_LTU_PRIORITY_OB1) + ++y; */ + + if (s->pic_priority_table) { /* SCI01 priority table set? */ + int j; + for (j = 0; j < 15; j++) + if (y < s->pic_priority_table[j+1]) + return j; + return 14; /* Maximum */ + } else + { + if (s->version >= SCI_VERSION_FTU_PRIORITY_14_ZONES) + return SCI0_VIEW_PRIORITY_14_ZONES(y); + else + return SCI0_VIEW_PRIORITY(y) == 15 ? 14 : + SCI0_VIEW_PRIORITY(y); + } +} + +inline int +_find_priority_band(state_t *s, int nr) +{ + if (s->version >= SCI_VERSION_FTU_PRIORITY_14_ZONES && (nr < 0 || nr > 14)) { + if (nr == 15) + return 0xffff; + else { + SCIkwarn(SCIkWARNING, "Attempt to get priority band %d\n", nr); + } + return 0; + } + + if (s->version < SCI_VERSION_FTU_PRIORITY_14_ZONES && (nr < 0 || nr > 15)) { + SCIkwarn(SCIkWARNING, "Attempt to get priority band %d\n", nr); + return 0; + } + + if (s->pic_priority_table) /* SCI01 priority table set? */ + return s->pic_priority_table[nr]; + else { + int retval; + + if (s->version >= SCI_VERSION_FTU_PRIORITY_14_ZONES) + retval = SCI0_PRIORITY_BAND_FIRST_14_ZONES(nr); + else + retval = SCI0_PRIORITY_BAND_FIRST(nr); + + /* if (s->version <= SCI_VERSION_LTU_PRIORITY_OB1) + --retval; */ + return retval; + } +} + +reg_t +graph_save_box(state_t *s, rect_t area) +{ + reg_t handle = kalloc(s, "graph_save_box()", sizeof(gfxw_snapshot_t *)); + gfxw_snapshot_t **ptr = (gfxw_snapshot_t **) kmem(s, handle); + + *ptr = gfxw_make_snapshot(s->visual, area); + + return handle; +} + + +void +graph_restore_box(state_t *s, reg_t handle) +{ + gfxw_snapshot_t **ptr; + int port_nr = s->port->ID; + + if (!handle.segment) { + SCIkwarn(SCIkWARNING, "Attempt to restore box with zero handle\n"); + return; + } + + ptr = (gfxw_snapshot_t **) kmem(s, handle); + + if (!ptr) { + SCIkwarn(SCIkWARNING, "Attempt to restore invalid handle %04x\n", handle); + return; + } + + while (port_nr > 2 && !(s->port->flags & GFXW_FLAG_IMMUNE_TO_SNAPSHOTS) + &&(gfxw_widget_matches_snapshot(*ptr, GFXW(s->port)))) { + /* This shouldn't ever happen, actually, since windows (ports w/ ID > 2) should all be immune */ + gfxw_port_t *newport = gfxw_find_port(s->visual, port_nr); + SCIkwarn(SCIkERROR, "Port %d is not immune against snapshots!\n", s->port->ID); + port_nr--; + if (newport) + s->port = newport; + } + + if (s->dyn_views && gfxw_widget_matches_snapshot(*ptr, GFXW(s->dyn_views->parent))) { + gfxw_container_t *parent = s->dyn_views->parent; + + do { + parent = parent->parent; + } while (parent && (gfxw_widget_matches_snapshot(*ptr, GFXW(parent)))); + + if (!parent) { + SCIkwarn(SCIkERROR, "Attempted widget mass destruction by a snapshot\n"); + BREAKPOINT(); + } + + reparentize_primary_widget_lists(s, (gfxw_port_t *) parent); + } + + + if (!ptr) { + SCIkwarn(SCIkERROR, "Attempt to restore invalid snaphot with handle %04x!\n", handle); + return; + } + + gfxw_restore_snapshot(s->visual, *ptr); + free(*ptr); + *ptr = NULL; + + kfree(s, handle); +} + +#if 0 +#define KERNEL_COLOR_PALETTE s->gfx_state->pic->visual_map->colors +#define KERNEL_COLORS_NR s->gfx_state->pic->visual_map->colors_nr +#else +#define KERNEL_COLOR_PALETTE s->gfx_state->resstate->static_palette +#define KERNEL_COLORS_NR s->gfx_state->resstate->static_palette_entries +#endif + +static gfx_pixmap_color_t white = {GFX_COLOR_INDEX_UNMAPPED, 255, 255, 255}; + +gfx_pixmap_color_t * +get_pic_color(state_t *s, int color) +{ + if (s->resmgr->sci_version < SCI_VERSION_01_VGA) + return &(s->ega_colors[color].visual); + + if (color == 255) + return &white; + else if (color < KERNEL_COLORS_NR) + return &(KERNEL_COLOR_PALETTE[color]); else + { + SCIkwarn(SCIkERROR, "Color index %d out of bounds for pic %d (%d max)", + color, s->gfx_state->pic_nr, KERNEL_COLORS_NR); + BREAKPOINT(); + return NULL; /* Well, rather, not return. But gcc gets scared here. */ + } +} + +static gfx_color_t +graph_map_color(state_t *s, int color, int priority, int control) +{ + gfx_color_t retval; + + if (s->resmgr->sci_version < SCI_VERSION_01_VGA) + { + retval = s->ega_colors[(color >=0 && color < 16)? color : 0]; + gfxop_set_color(s->gfx_state, &retval, (color < 0)? -1 : retval.visual.r, retval.visual.g, retval.visual.b, + (color == -1)? 255 : 0, priority, control); + } else + { + retval.visual = *(get_pic_color(s, color)); + retval.alpha = 0; + retval.priority = priority; + retval.control = control; + retval.mask = + GFX_MASK_VISUAL | + ((priority >= 0)? GFX_MASK_PRIORITY : 0) | + ((control >= 0)? GFX_MASK_CONTROL : 0); + }; + + return retval; +} + +/* --- */ + + +reg_t +kSetCursor_SCI11(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + switch (argc) + { + case 1 : + if (UKPV(0) == 0) + { + s->save_mouse_pointer_view = s->mouse_pointer_view; + s->save_mouse_pointer_loop = s->mouse_pointer_loop; + s->save_mouse_pointer_cel = s->mouse_pointer_cel; + s->mouse_pointer_view = s->mouse_pointer_loop = s->mouse_pointer_cel = -1; + gfxop_set_pointer_cursor(s->gfx_state, GFXOP_NO_POINTER); + } + else + { + s->mouse_pointer_view = s->save_mouse_pointer_view; + s->mouse_pointer_loop = s->save_mouse_pointer_loop; + s->mouse_pointer_cel = s->save_mouse_pointer_cel; + } + case 2 : + { + point_t pt; + pt.x = UKPV(0); + pt.y = UKPV(1); + + GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state, pt)); + break; + } + case 3 : + GFX_ASSERT(gfxop_set_pointer_view(s->gfx_state, UKPV(0), UKPV(1), UKPV(2), NULL)); + s->mouse_pointer_view = UKPV(0); + s->mouse_pointer_loop = UKPV(1); + s->mouse_pointer_cel = UKPV(2); + break; + case 9 : { + point_t hotspot = gfx_point(SKPV(3), SKPV(4)); + +// sciprintf("Setting hotspot at %d/%d\n", hotspot.x, hotspot.y); + + gfxop_set_pointer_view(s->gfx_state, UKPV(0), UKPV(1), UKPV(2), &hotspot); + break; + } + default : + SCIkwarn(SCIkERROR, "kSetCursor: Unhandled case: %d arguments given!\n", argc); + break; + } + return s->r_acc; +} + +reg_t +kSetCursor(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + if (s->version >= SCI_VERSION(1,001,000)|| + has_kernel_function(s, "MoveCursor")) + { + return kSetCursor_SCI11(s, funct_nr, argc, argv); + } + + if (SKPV_OR_ALT(1,1)) { + s->mouse_pointer_view = SKPV(0); + } else + s->mouse_pointer_view = GFXOP_NO_POINTER; + + s->mouse_pointer_loop = s->mouse_pointer_cel = 0; /* Not used with cursor-format pointers */ + + GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state, s->mouse_pointer_view)); + + if (argc > 2) { + point_t newpos = gfx_point(SKPV(2) + s->port->bounds.x, + SKPV(3) + s->port->bounds.y); + + GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state, newpos)); + } + + return s->r_acc; + +} + +extern int oldx, oldy; + +reg_t +kMoveCursor(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + point_t newpos; + static point_t oldpos = {0}; + + newpos = s->gfx_state->pointer_pos; + + if (argc == 1) + { + /* Case ignored on IBM PC */ + } else + { + newpos.x = SKPV(0)+s->port->zone.x; + newpos.y = SKPV(1)+s->port->zone.y; + + if (newpos.x > s->port->zone.x+s->port->zone.xl) + newpos.x = s->port->zone.x+s->port->zone.xl; + if (newpos.y > s->port->zone.y+s->port->zone.yl) + newpos.y = s->port->zone.y+s->port->zone.yl; + + if (newpos.x < 0) newpos.x=0; + if (newpos.y < 0) newpos.y=0; + + oldpos = newpos; + } + + GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state, newpos)); + + return s->r_acc; +} + +static inline void +_ascertain_port_contents(gfxw_port_t *port) +{ + if (!port->contents) + port->contents = (gfxw_widget_t *) gfxw_new_list(port->bounds, 0); +} + +reg_t +kShow(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int old_map = s->pic_visible_map; + + s->pic_visible_map = (gfx_map_mask_t) UKPV_OR_ALT(0, 1); + + switch (s->pic_visible_map) { + + case GFX_MASK_VISUAL: + case GFX_MASK_PRIORITY: + case GFX_MASK_CONTROL: + gfxop_set_visible_map(s->gfx_state, s->pic_visible_map); + if (old_map != s->pic_visible_map) { + + if (s->pic_visible_map == GFX_MASK_VISUAL) /* Full widget redraw */ + s->visual->draw(GFXW(s->visual), gfx_point(0,0)); + + gfxop_update(s->gfx_state); + sciprintf("Switching visible map to %x\n", s->pic_visible_map); + } + break; + + default: + SCIkwarn(SCIkWARNING, "Show(%x) selects unknown map\n", s->pic_visible_map); + + } + + s->pic_not_valid = 2; + return s->r_acc; +} + + +reg_t +kPicNotValid(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + s->r_acc = make_reg(0, s->pic_not_valid); + if (argc) + s->pic_not_valid = (byte)UKPV(0); + + return s->r_acc; +} + +void +_k_redraw_box(state_t *s, int x1, int y1, int x2, int y2) +{ + sciprintf("_k_redraw_box(): Unimplemented!\n"); +#if 0 + int i; + view_object_t *list = s->dyn_views; + + sciprintf("Reanimating views\n", s->dyn_views_nr); + + + for (i=0;idyn_views_nr;i++) { + *(list[i].underBitsp) = graph_save_box(s, + list[i].nsLeft, + list[i].nsTop, + list[i].nsRight-list[i].nsLeft, + list[i].nsBottom-list[i].nsTop, + SCI_MAP_VISUAL | SCI_MAP_PRIORITY); + draw_view0(s->pic, s->ports[0], + list[i].nsLeft, list[i].nsTop, + list[i].priority, list[i].loop, + list[i].cel, 0, list[i].view); + } + + graph_update_box(s, x1, y1, x2-x1, y2-y1); + + for (i=0;idyn_views_nr;i++) { + graph_restore_box(s, *(list[i].underBitsp)); + list[i].underBits=0; + } +#endif +} + +void +_k_graph_rebuild_port_with_color(state_t *s, gfx_color_t newbgcolor) +{ + gfxw_port_t *port = s->port; + gfxw_port_t *newport; + + newport = sciw_new_window(s, port->zone, port->font_nr, port->color, newbgcolor, + s->titlebar_port->font_nr, s->ega_colors[15], s->ega_colors[8], + port->title_text, port->port_flags & ~WINDOW_FLAG_TRANSPARENT); + + if (s->dyn_views) { + int found = 0; + gfxw_container_t *parent = s->dyn_views->parent; + + while (parent && !(found |= (GFXW(parent) == GFXW(port)))) + parent = parent->parent; + + s->dyn_views = NULL; + } + + port->parent->add(GFXWC(port->parent), GFXW(newport)); + port->widfree(GFXW(port)); +} + +static int activated_icon_bar; +static int port_origin_x; +static int port_origin_y; + +reg_t +kGraph(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + rect_t area; + gfxw_port_t *port = s->port; + int redraw_port = 0; + + area = gfx_rect(SKPV(2), SKPV(1) , SKPV(4), SKPV(3)); + + area.xl = area.xl - area.x; /* Since the actual coordinates are absolute */ + area.yl = area.yl - area.y; + + switch(SKPV(0)) { + + case K_GRAPH_GET_COLORS_NR: + + return make_reg(0, (s->resmgr->sci_version < SCI_VERSION_01_VGA) ? 0x10 : 0x100); + break; + + case K_GRAPH_DRAW_LINE: { + + gfx_color_t gfxcolor = graph_map_color(s, SKPV(5) & 0xf, SKPV_OR_ALT(6, -1), SKPV_OR_ALT(7, -1)); + + SCIkdebug(SCIkGRAPHICS, "draw_line((%d, %d), (%d, %d), col=%d, p=%d, c=%d, mask=%d)\n", + SKPV(2), SKPV(1), SKPV(4), SKPV(3), SKPV(5), SKPV_OR_ALT(6, -1), SKPV_OR_ALT(7, -1), + gfxcolor.mask); + + redraw_port = 1; + ADD_TO_CURRENT_BG_WIDGETS(GFXW(gfxw_new_line(gfx_point(SKPV(2), SKPV(1)), + gfx_point(SKPV(4), SKPV(3)), + gfxcolor, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL))); + + } + break; + + case K_GRAPH_SAVE_BOX: + + area.x += s->port->zone.x + port_origin_x; + area.y += s->port->zone.y + port_origin_y; + area.xl += -port_origin_x; + area.yl += -port_origin_y; + + return(graph_save_box(s, area)); + break; + + case K_GRAPH_RESTORE_BOX: + + graph_restore_box(s, argv[1]); + break; + + case K_GRAPH_FILL_BOX_BACKGROUND: + + _k_graph_rebuild_port_with_color(s, port->bgcolor); + port = s->port; + + redraw_port = 1; + break; + + case K_GRAPH_FILL_BOX_FOREGROUND: + + _k_graph_rebuild_port_with_color(s, port->color); + port = s->port; + + redraw_port = 1; + break; + + case K_GRAPH_FILL_BOX_ANY: { + + gfx_color_t color = graph_map_color(s, SKPV(6), SKPV_OR_ALT(7, -1), SKPV_OR_ALT(8, -1)); + + color.mask = (byte)UKPV(5); + + SCIkdebug(SCIkGRAPHICS, "fill_box_any((%d, %d), (%d, %d), col=%d, p=%d, c=%d, mask=%d)\n", + SKPV(2), SKPV(1), SKPV(4), SKPV(3), SKPV(6), SKPV_OR_ALT(7, -1), SKPV_OR_ALT(8, -1), + UKPV(5)); + + ADD_TO_CURRENT_BG_WIDGETS(gfxw_new_box(s->gfx_state, area, color, color, GFX_BOX_SHADE_FLAT)); + + } + break; + + case K_GRAPH_UPDATE_BOX: { + + SCIkdebug(SCIkGRAPHICS, "update_box(%d, %d, %d, %d)\n", + SKPV(1), SKPV(2), SKPV(3), SKPV(4)); + + area.x += s->port->zone.x; + area.y += s->port->zone.y; + + gfxop_update_box(s->gfx_state, area); + + } + break; + + case K_GRAPH_REDRAW_BOX: { + + + SCIkdebug(SCIkGRAPHICS, "redraw_box(%d, %d, %d, %d)\n", + SKPV(1), SKPV(2), SKPV(3), SKPV(4)); + + area.x += s->port->zone.x; + area.y += s->port->zone.y; + + if (s->dyn_views && s->dyn_views->parent == GFXWC(s->port)) + s->dyn_views->draw(GFXW(s->dyn_views), gfx_point(0, 0)); + + gfxop_update_box(s->gfx_state, area); + + } + + break; + + case K_GRAPH_ADJUST_PRIORITY: + + SCIkdebug(SCIkGRAPHICS, "adjust_priority(%d, %d)\n", SKPV(1), SKPV(2)); + s->priority_first = SKPV(1) - 10; + s->priority_last = SKPV(2) - 10; + break; + + default: + + SCIkdebug(SCIkSTUB, "Unhandled Graph() operation %04x\n", SKPV(0)); + + } + + if (redraw_port) + FULL_REDRAW(); + + gfxop_update(s->gfx_state); + return s->r_acc; +} + + +reg_t +kTextSize(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int width, height; + char *text = argv[1].segment ? (char *) kernel_dereference_bulk_pointer(s, argv[1], 0) : NULL; + reg_t *dest = kernel_dereference_reg_pointer(s, argv[0], 4); + int maxwidth = KP_UINT(KP_ALT(3, NULL_REG)); + int font_nr = KP_UINT(argv[2]); + + if (maxwidth < 0) + maxwidth = 0; + + dest[0] = dest[1] = NULL_REG; + + if (!text || !*text || !dest) { /* Empty text */ + dest[2] = dest[3] = make_reg(0, 0); + + SCIkdebug(SCIkSTRINGS, "GetTextSize: Empty string\n"); + return s->r_acc; + } + + GFX_ASSERT(gfxop_get_text_params(s->gfx_state, font_nr, text, + maxwidth? maxwidth : MAX_TEXT_WIDTH_MAGIC_VALUE, + &width, &height, 0, + NULL, NULL, NULL)); + SCIkdebug(SCIkSTRINGS, "GetTextSize '%s' -> %dx%d\n", text, width, height); + + dest[2] = make_reg(0, height); +// dest[3] = make_reg(0, maxwidth? maxwidth : width); + dest[3] = make_reg(0, width); + + return s->r_acc; +} + + +int debug_sleeptime_factor = 1; + +reg_t +kWait(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + GTimeVal time; + int sleep_time = UKPV(0); + + sci_get_current_time (&time); + + s->r_acc = make_reg(0, ((time.tv_usec - s->last_wait_time.tv_usec) * 60 / 1000000) + + (time.tv_sec - s->last_wait_time.tv_sec) * 60); + + memcpy(&(s->last_wait_time), &time, sizeof(GTimeVal)); + + /* Reset optimization flags: Game is playing along nicely anyway */ + s->kernel_opt_flags &= ~(KERNEL_OPT_FLAG_GOT_EVENT + | KERNEL_OPT_FLAG_GOT_2NDEVENT); + + sleep_time *= debug_sleeptime_factor; + GFX_ASSERT(gfxop_usleep(s->gfx_state, sleep_time * 1000000 / 60)); + + return s->r_acc; +} + + +reg_t +kCoordPri(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int y = SKPV(0); + + return make_reg(0, VIEW_PRIORITY(y)); +} + + +reg_t +kPriCoord(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int priority = SKPV(0); + + return make_reg(0, PRIORITY_BAND_FIRST(priority)); +} + + + +void +_k_dirloop(reg_t obj, word angle, state_t *s, int funct_nr, + int argc, reg_t *argv) +{ + int view = GET_SEL32V(obj, view); + int signal = GET_SEL32V(obj, signal); + int loop; + int maxloops; + + if (signal & _K_VIEW_SIG_FLAG_DOESNT_TURN) + return; + + angle %= 360; + + if (s->version >= SCI_VERSION_FTU_2ND_ANGLES) { + if (angle < 45) + loop = 3; + else if (angle < 136) + loop = 0; + else if (angle < 225) + loop = 2; + else if (angle < 316) + loop = 1; + else + loop = 3; + } else { + if (angle >= 330 || angle <= 30) + loop = 3; + else if (angle <= 150) + loop = 0; + else if (angle <= 210) + loop = 2; + else if (angle < 330) + loop = 1; + else loop = 0xffff; + } + + maxloops = gfxop_lookup_view_get_loops(s->gfx_state, view); + + if (maxloops == GFX_ERROR) { + SCIkwarn(SCIkERROR, "Invalid view.%03d\n", view); + return; + } else if ((loop>1)&&(maxloops < 4)) + return; + + PUT_SEL32V(obj, loop, loop); +} + + +reg_t +kDirLoop(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + _k_dirloop(argv[0], UKPV(1), s, funct_nr, argc, argv); + + return s->r_acc; +} + +#define GASEOUS_VIEW_MASK_ACTIVE (_K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_IGNORE_ACTOR) +#define GASEOUS_VIEW_MASK_PASSIVE (_K_VIEW_SIG_FLAG_NO_UPDATE | _K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_IGNORE_ACTOR) + +abs_rect_t +set_base(struct _state *s, reg_t object); + +inline abs_rect_t +get_nsrect(struct _state *s, reg_t object, byte clip); + +static inline abs_rect_t +nsrect_clip(state_t *s, int y, abs_rect_t retval, int priority); + +static int +collides_with(state_t *s, abs_rect_t area, reg_t other_obj, int use_nsrect, int view_mask, int funct_nr, int argc, + reg_t *argv) +{ + int other_signal = GET_SEL32V(other_obj, signal); + int other_priority = GET_SEL32V(other_obj, priority); + int y = GET_SEL32SV(other_obj, y); + abs_rect_t other_area; + + if (use_nsrect) { + other_area = get_nsrect(s, other_obj, 0); + other_area = nsrect_clip(s, y, other_area, other_priority); + } else { + other_area.x = GET_SEL32V(other_obj, brLeft); + other_area.xend = GET_SEL32V(other_obj, brRight); + other_area.y = GET_SEL32V(other_obj, brTop); + other_area.yend = GET_SEL32V(other_obj, brBottom); + } + + if (other_area.xend < 0 || other_area.yend < 0 || area.xend < 0 || area.yend < 0) + return 0; /* Out of scope */ + + if (other_area.x >= 320 || other_area.y >= 190 || area.xend >= 320 || area.yend >= 190) + return 0; /* Out of scope */ + + SCIkdebug(SCIkBRESEN, "OtherSignal=%04x, z=%04x obj="PREG"\n", other_signal, + (other_signal & view_mask), PRINT_REG(other_obj)); + + if ((other_signal & (view_mask)) == 0) { + /* check whether the other object ignores actors */ + + SCIkdebug(SCIkBRESEN, " against (%d,%d) to (%d,%d)\n", + other_area.x, other_area.y, other_area.xend, other_area.yend); + + + if (((other_area.xend > area.x) + && (other_area.x < area.xend)) /* [other_x, other_xend] intersects [x, xend])? */ + && + ((other_area.yend > area.y) + && (other_area.y < area.yend))) /* [other_y, other_yend] intersects [y, yend]? */ + return 1; + /* CR (from :Bob Heitman:) Collision rects have Mac semantics, ((0,0),(1,1)) only + ** covers the coordinate (0,0) */ + + + } + + SCIkdebug(SCIkBRESEN, " (no)\n"); + return 0; +} + + +reg_t +kCanBeHere(state_t *s, int funct_nr, int argc, reg_t * argv) +{ + reg_t obj = argv[0]; + reg_t cliplist_ref = KP_ALT(1, NULL_REG); + list_t *cliplist = NULL; + gfxw_port_t *port = s->picture_port; + word signal; + int retval; + + abs_rect_t abs_zone; + rect_t zone; + word edgehit; + word illegal_bits; + + + abs_zone.x = GET_SEL32SV(obj, brLeft); + abs_zone.xend = GET_SEL32SV(obj, brRight); + abs_zone.y = GET_SEL32SV(obj, brTop); + abs_zone.yend = GET_SEL32SV(obj, brBottom); + + zone = gfx_rect(abs_zone.x + port->zone.x, abs_zone.y + port->zone.y, + abs_zone.xend - abs_zone.x, abs_zone.yend - abs_zone.y); + + signal = GET_SEL32V(obj, signal); + SCIkdebug(SCIkBRESEN,"Checking collision: (%d,%d) to (%d,%d) ([%d..%d]x[%d..%d]), obj="PREG", sig=%04x, cliplist="PREG"\n", + GFX_PRINT_RECT(zone), + abs_zone.x, abs_zone.xend, abs_zone.y, abs_zone.yend, + PRINT_REG(obj), signal, PRINT_REG(cliplist_ref)); + + illegal_bits = GET_SEL32V(obj, illegalBits); + + retval = !(illegal_bits + & (edgehit = gfxop_scan_bitmask(s->gfx_state, zone, GFX_MASK_CONTROL))); + + SCIkdebug(SCIkBRESEN, "edgehit = %04x (illegalBits %04x)\n", edgehit, illegal_bits); + if (retval == 0) { + SCIkdebug(SCIkBRESEN, " -> %04x\n", retval); + return not_register(s, NULL_REG); /* Can'tBeHere */ + } + + retval = 0; + + if ((illegal_bits & 0x8000) /* If we are vulnerable to those views at all... */ + && s->dyn_views) { /* ...check against all stop-updated dynviews */ + gfxw_dyn_view_t *widget = (gfxw_dyn_view_t *) s->dyn_views->contents; + + SCIkdebug(SCIkBRESEN, "Checking vs dynviews:\n"); + + while (widget) { + if (widget->ID + && (widget->signal & _K_VIEW_SIG_FLAG_FREESCI_STOPUPD) + && ((widget->ID != obj.segment) || (widget->subID != obj.offset)) + && is_object(s, make_reg(widget->ID, widget->subID))) + if (collides_with(s, abs_zone, make_reg(widget->ID, widget->subID), 1, + GASEOUS_VIEW_MASK_ACTIVE, funct_nr, argc, argv)) + return not_register(s, NULL_REG); + + widget = (gfxw_dyn_view_t *) widget->next; + } + } + + if (signal & GASEOUS_VIEW_MASK_ACTIVE) { + retval = signal & GASEOUS_VIEW_MASK_ACTIVE; /* CanBeHere- it's either being disposed, or it ignores actors anyway */ + SCIkdebug(SCIkBRESEN, " -> %04x\n", retval); + return not_register(s, make_reg(0, retval)); /* CanBeHere */ + } + + if (cliplist_ref.segment) + cliplist = LOOKUP_LIST(cliplist_ref); + + if (cliplist) { + node_t *node = LOOKUP_NODE(cliplist->first); + + retval = 0; /* Assume that we Can'tBeHere... */ + + while (node) { /* Check each object in the list against our bounding rectangle */ + reg_t other_obj = node->value; + SCIkdebug(SCIkBRESEN, " comparing against "PREG"\n", PRINT_REG(other_obj)); + + if (!is_object(s, other_obj)) { + SCIkdebug(SCIkWARNING, "CanBeHere() cliplist contains non-object %04x\n", other_obj); + } else if (!REG_EQ(other_obj, obj)) { /* Clipping against yourself is not recommended */ + + if (collides_with(s, abs_zone, other_obj, 0, GASEOUS_VIEW_MASK_PASSIVE, funct_nr, argc, argv)) { + SCIkdebug(SCIkBRESEN, " -> %04x\n", retval); + return not_register(s, NULL_REG); + } + + } /* if (other_obj != obj) */ + node = LOOKUP_NODE(node->succ); /* move on */ + } + } + + if (!retval) + retval = 1; + SCIkdebug(SCIkBRESEN, " -> %04x\n", retval); + + return not_register(s, make_reg(0, retval)); +} /* CanBeHere */ + +reg_t +kIsItSkip(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int view = SKPV(0); + int loop = SKPV(1); + int cel = SKPV(2); + int x = UKPV(3); + int y = UKPV(4); + gfxr_view_t *res = NULL; + gfx_pixmap_t *pxm = NULL; + + if (!(res = gfxr_get_view(s->gfx_state->resstate, view, &loop, &cel, 0))) { + GFXWARN("Attempt to get cel parameters for invalid view %d\n", view); + return make_reg(0, -1); + } + + pxm = res->loops[loop].cels[cel]; + if (x > pxm->index_xl) x = pxm->index_xl-1; + if (y > pxm->index_yl) y = pxm->index_yl-1; + + return make_reg(0, + pxm->index_data[y*pxm->index_xl+x] == + pxm->color_key); +} + +reg_t +kCelHigh(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int view = SKPV(0); + int loop = SKPV(1); + int cel = SKPV(2); + int height, width; + point_t offset; + + if (argc != 3) { + SCIkwarn(SCIkWARNING, "CelHigh called with %d parameters!\n", argc); + } + + if (gfxop_get_cel_parameters(s->gfx_state, view, loop, cel, &width, &height, &offset)) { + SCIkwarn(SCIkERROR, "Invalid loop (%d) or cel (%d) in view.%d (0x%x), or view invalid\n", loop, cel, view, view); + return NULL_REG; + } else + return make_reg(0, height); +} + +reg_t +kCelWide(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int view = SKPV(0); + int loop = SKPV(1); + int cel = SKPV(2); + int height, width; + point_t offset; + + if (argc != 3) { + SCIkwarn(SCIkWARNING, "CelHigh called with %d parameters!\n", argc); + } + + if (gfxop_get_cel_parameters(s->gfx_state, view, loop, cel, &width, &height, &offset)) { + SCIkwarn(SCIkERROR, "Invalid loop (%d) or cel (%d) in view.%d (0x%x), or view invalid\n", loop, cel, view, view); + return NULL_REG; + } else + return make_reg(0, width); +} + +reg_t +kNumLoops(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t obj = argv[0]; + int view = GET_SEL32V(obj, view); + int loops_nr = gfxop_lookup_view_get_loops(s->gfx_state, view); + + + if (loops_nr < 0) { + SCIkwarn(SCIkERROR, "view.%d (0x%x) not found\n", view, view); + return NULL_REG; + } + + SCIkdebug(SCIkGRAPHICS, "NumLoops(view.%d) = %d\n", view, loops_nr); + + return make_reg(0, loops_nr); +} + + +reg_t +kNumCels(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t obj = argv[0]; + int loop = GET_SEL32V(obj, loop); + int view = GET_SEL32V(obj, view); + int cel = 0xffff; + + + if (gfxop_check_cel(s->gfx_state, view, &loop, &cel)) { /* OK, this is a hack and there's a + ** real function to calculate cel numbers... */ + SCIkwarn(SCIkERROR, "view.%d (0x%x) not found\n", view, view); + return NULL_REG; + } + + SCIkdebug(SCIkGRAPHICS, "NumCels(view.%d, %d) = %d\n", view, loop, cel+1); + + return make_reg(0, cel + 1); +} + +reg_t +kOnControl(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int arg = 0; + gfx_map_mask_t map; + int xstart, ystart; + int xlen = 1, ylen = 1; + + + if (argc == 2 || argc == 4) + map = GFX_MASK_CONTROL; + else { + arg = 1; + map = (gfx_map_mask_t) SKPV(0); + } + + ystart = SKPV(arg+1); + xstart = SKPV(arg); + + if (argc > 3) { + ylen = SKPV(arg+3) - ystart; + xlen = SKPV(arg+2) - xstart; + } + + return make_reg(0, gfxop_scan_bitmask(s->gfx_state, gfx_rect(xstart, ystart + 10, xlen, ylen), map)); +} + +void +_k_view_list_free_backgrounds(state_t *s, view_object_t *list, int list_nr); + +int sci01_priority_table_flags = 0; + +reg_t +kDrawPic(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int pic_nr = SKPV(0); + int add_to_pic = 1; + int palette = SKPV_OR_ALT(3, 0); + gfx_color_t transparent = s->wm_port->bgcolor; + + CHECK_THIS_KERNEL_FUNCTION; + + if (s->version < SCI_VERSION_FTU_NEWER_DRAWPIC_PARAMETERS) { + if (!SKPV_OR_ALT(2, 0)) + add_to_pic = 0; + } else + if (SKPV_OR_ALT(2, 1)) + add_to_pic = 0; + + gfxop_disable_dirty_frames(s->gfx_state); + + if (NULL != s->old_screen) { + gfxop_free_pixmap(s->gfx_state, s->old_screen); + } + + s->old_screen = gfxop_grab_pixmap(s->gfx_state, gfx_rect(0, 10, 320, 190)); + + SCIkdebug(SCIkGRAPHICS,"Drawing pic.%03d\n", SKPV(0)); + + if (!s->pics) { + s->pics = (drawn_pic_t*)sci_malloc(sizeof(drawn_pic_t) * (s->pics_nr = 8)); + s->pics_drawn_nr = 0; + } + + if (add_to_pic) { + if (s->pics_nr == s->pics_drawn_nr) { + s->pics_nr += 4; + s->pics = (drawn_pic_t*)sci_realloc(s->pics, sizeof(drawn_pic_t) * s->pics_nr); + } + s->pics[s->pics_drawn_nr].palette = palette; + s->pics[s->pics_drawn_nr++].nr = pic_nr; + GFX_ASSERT(gfxop_add_to_pic(s->gfx_state, pic_nr, 1, palette)); + } else { + s->pics_drawn_nr = 1; + s->pics[0].nr = pic_nr; + s->pics[0].palette = palette; + GFX_ASSERT(gfxop_new_pic(s->gfx_state, pic_nr, 1, palette)); + } + + gfxw_widget_kill_chrono(s->visual, 0); + s->wm_port->widfree(GFXW(s->wm_port)); + s->picture_port->widfree(GFXW(s->picture_port)); + s->iconbar_port->widfree(GFXW(s->iconbar_port)); + + s->wm_port = gfxw_new_port(s->visual, NULL, s->gfx_state->options->pic_port_bounds, s->ega_colors[0], transparent); + s->picture_port = gfxw_new_port(s->visual, NULL, s->gfx_state->options->pic_port_bounds, s->ega_colors[0], transparent); + s->iconbar_port = gfxw_new_port(s->visual, NULL, gfx_rect(0, 0, 320, 200), s->ega_colors[0], transparent); + s->iconbar_port->flags |= GFXW_FLAG_NO_IMPLICIT_SWITCH; + + s->visual->add(GFXWC(s->visual), GFXW(s->picture_port)); + s->visual->add(GFXWC(s->visual), GFXW(s->wm_port)); + s->visual->add(GFXWC(s->visual), GFXW(s->iconbar_port)); + + s->port = s->picture_port; + + s->pic_priority_table = (int*)gfxop_get_pic_metainfo(s->gfx_state); + + if (sci01_priority_table_flags & 0x2) { + if (s->pic_priority_table) { + int i; + fprintf(stderr,"---------------------------\nPriority table:\n"); + for (i = 0; i < 16; i++) + fprintf(stderr,"\t%d:\t%d\n", i, s->pic_priority_table[i]); + fprintf(stderr,"---------------------------\n"); + } + } + if (sci01_priority_table_flags & 0x1) + s->pic_priority_table = NULL; + + if (argc > 1) + s->pic_animate = SKPV(1); /* The animation used during kAnimate() later on */ + + s->dyn_views = NULL; + s->drop_views = NULL; + + s->priority_first = 42; + + if (s->version < SCI_VERSION_FTU_PRIORITY_14_ZONES) + s->priority_last = 200; + else + s->priority_last = 190; + + s->pic_not_valid = 1; + s->pic_is_new = 1; + + return s->r_acc; + +} + + + + +abs_rect_t +set_base(state_t *s, reg_t object) +{ + int x, y, original_y, z, ystep, xsize, ysize; + int xbase, ybase, xend, yend; + int view, loop, cel; + int oldloop, oldcel; + int xmod = 0, ymod = 0; + abs_rect_t retval; + + x = GET_SEL32SV(object, x); + original_y = y = GET_SEL32SV(object, y); + + if (s->selector_map.z > -1) + z = GET_SEL32SV(object, z); + else + z = 0; + + y -= z; /* Subtract z offset */ + + ystep = GET_SEL32SV(object, yStep); + + view = GET_SEL32SV(object, view); + oldloop = loop = sign_extend_byte(GET_SEL32V(object, loop)); + oldcel = cel = sign_extend_byte(GET_SEL32V(object, cel)); + + if (gfxop_check_cel(s->gfx_state, view, &loop, &cel)) { + xsize = ysize = xmod = ymod = 0; + } else { + point_t offset = gfx_point(0, 0); + + if (loop != oldloop) { + loop = 0; + PUT_SEL32V(object, loop, 0); + SCIkdebug(SCIkGRAPHICS, "Resetting loop for "PREG"!\n", PRINT_REG(object)); + } + + if (cel != oldcel) { + cel = 0; + PUT_SEL32V(object, cel, 0); + } + + gfxop_get_cel_parameters(s->gfx_state, view, loop, cel, + &xsize, &ysize, &offset); + + xmod = offset.x; + ymod = offset.y; + } + + + xbase = x - xmod - (xsize >> 1); + xend = xbase + xsize; + yend = y /* - ymod */ + 1; + ybase = yend - ystep; + + SCIkdebug(SCIkBASESETTER, "(%d,%d)+/-(%d,%d), (%d x %d) -> (%d, %d) to (%d, %d)\n", + x, y, xmod, ymod, xsize, ysize, xbase, ybase, xend, yend); + + retval.x = xbase; + retval.y = ybase; + retval.xend = xend; + retval.yend = yend; + + return retval; +} + + +void +_k_base_setter(state_t *s, reg_t object) +{ + abs_rect_t absrect = set_base(s, object); + + if (lookup_selector(s, object, s->selector_map.brLeft, NULL, NULL) + != SELECTOR_VARIABLE) + return; /* non-fatal */ + + if (s->version <= SCI_VERSION_LTU_BASE_OB1) + --absrect.y; /* Compensate for early SCI OB1 'bug' */ + + PUT_SEL32V(object, brLeft, absrect.x); + PUT_SEL32V(object, brRight, absrect.xend); + PUT_SEL32V(object, brTop, absrect.y); + PUT_SEL32V(object, brBottom, absrect.yend); +} + +reg_t +kBaseSetter(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t object = argv[0]; + + + _k_base_setter(s, object); + + return s->r_acc; +} /* kBaseSetter */ + + +static inline abs_rect_t +nsrect_clip(state_t *s, int y, abs_rect_t retval, int priority) +{ + int pri_top; + + if (priority == -1) + priority = VIEW_PRIORITY(y); + + pri_top = PRIORITY_BAND_FIRST(priority) + 1; + /* +1: Don't know why, but this seems to be happening */ + + if (retval.y < pri_top) + retval.y = pri_top; + + if (retval.yend < retval.y) + retval.y = retval.yend - 1; + + return retval; +} + +inline abs_rect_t +calculate_nsrect(state_t *s, int x, int y, int view, int loop, int cel) +{ + int xbase, ybase, xend, yend, xsize, ysize; + int xmod = 0, ymod = 0; + abs_rect_t retval = {0,0,0,0}; + + if (gfxop_check_cel(s->gfx_state, view, &loop, &cel)) { + xsize = ysize = xmod = ymod = 0; + } else { + point_t offset = gfx_point(0, 0); + + gfxop_get_cel_parameters(s->gfx_state, view, loop, cel, + &xsize, &ysize, &offset); + + xmod = offset.x; + ymod = offset.y; + } + + xbase = x - xmod - (xsize >> 1); + xend = xbase + xsize; + yend = y - ymod + 1; /* +1: magic modifier */ + ybase = yend - ysize; + + retval.x = xbase; + retval.y = ybase; + retval.xend = xend; + retval.yend = yend; + + return retval; +} + +inline abs_rect_t +get_nsrect(state_t *s, reg_t object, byte clip) +{ + int x, y, z; + int view, loop, cel; + abs_rect_t retval; + + x = GET_SEL32SV(object, x); + y = GET_SEL32SV(object, y); + + if (s->selector_map.z > -1) + z = GET_SEL32SV(object, z); + else + z = 0; + + y -= z; /* Subtract z offset */ + + view = GET_SEL32SV(object, view); + loop = sign_extend_byte(GET_SEL32SV(object, loop)); + cel = sign_extend_byte(GET_SEL32SV(object, cel)); + + retval = calculate_nsrect(s, x, y, view, loop, cel); + + if (clip) { + int priority = GET_SEL32SV(object, priority); + return nsrect_clip(s, y, retval, priority); + } + + return retval; +} + +static void +_k_set_now_seen(state_t *s, reg_t object) +{ + abs_rect_t absrect = get_nsrect(s, object, 0); + + if (lookup_selector(s, object, s->selector_map.nsTop, NULL, NULL) + != SELECTOR_VARIABLE) { return; } /* This isn't fatal */ + + PUT_SEL32V(object, nsLeft, absrect.x); + PUT_SEL32V(object, nsRight, absrect.xend); + PUT_SEL32V(object, nsTop, absrect.y); + PUT_SEL32V(object, nsBottom, absrect.yend); +} + + +reg_t +kSetNowSeen(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t object = argv[0]; + + _k_set_now_seen(s, object); + + return s->r_acc; +} /* kSetNowSeen */ + +reg_t +kPalette(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + switch (UKPV(0)) + { + case 5 : { + int r = UKPV(1); + int g = UKPV(2); + int b = UKPV(3); + + int i, delta, bestindex = -1, bestdelta = 200000; + + for (i = 0; i < KERNEL_COLORS_NR; i++) { + int dr = abs (KERNEL_COLOR_PALETTE[i].r - r); + int dg = abs (KERNEL_COLOR_PALETTE[i].g - g); + int db = abs (KERNEL_COLOR_PALETTE[i].b - b); + + delta = dr*dr + dg * dg + db * db; + + if (delta < bestdelta) + { + bestdelta = delta; + bestindex = i; + } + } + + /* Don't warn about inexact mappings -- it's actually the + ** rule rather than the exception */ + return make_reg(0, bestindex); + } + + case 4 : + case 6 : + break; + default : + SCIkdebug(SCIkWARNING, "Unimplemented subfunction: %d\n", UKPV(0)); + } + return s->r_acc; +} + +static void +_k_draw_control(state_t *s, reg_t obj, int inverse); + + +static void +_k_disable_delete_for_now(state_t *s, reg_t obj) +{ + reg_t text_pos = GET_SEL32(obj, text); + char *text = IS_NULL_REG(text_pos)? NULL : (char *) sm_dereference(&s->seg_manager, text_pos, NULL); + int type = GET_SEL32V(obj, type); + int state = GET_SEL32V(obj, state); + + if (type == K_CONTROL_BUTTON && text && + !strcmp(s->game_name, "sq4") && + s->version < SCI_VERSION(1,001,000) && + !strcmp(text, " Delete ")) + PUT_SEL32V(obj, state, (state | CONTROL_STATE_GRAY) & ~CONTROL_STATE_ENABLED); +} + +reg_t +kDrawControl(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t obj = argv[0]; + + _k_disable_delete_for_now(s, obj); + _k_draw_control(s, obj, 0); + FULL_REDRAW(); + return NULL_REG; +} + + +reg_t +kHiliteControl(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t obj = argv[0]; + + + _k_draw_control(s, obj, 1); + return s->r_acc; +} + + +void +update_cursor_limits(int *display_offset, int *cursor, int max_displayed) +{ + if (*cursor < *display_offset + 4) { + if (*cursor < 8) + *display_offset = 0; + else + *display_offset = *cursor - 8; + + } else if (*cursor - *display_offset > max_displayed - 8) + *display_offset = 12 + *cursor - max_displayed; + +} + +#define _K_EDIT_DELETE \ + if (cursor < textlen) { \ + memmove(text + cursor, text + cursor + 1, textlen - cursor +1); \ +} + +#define _K_EDIT_BACKSPACE \ + if (cursor) { \ + --cursor; \ + memmove(text + cursor, text + cursor + 1, textlen - cursor +1); \ + --textlen; \ +} + + + +reg_t +kEditControl(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t obj = argv[0]; + reg_t event = argv[1]; + + + if (obj.segment) { + word ct_type = GET_SEL32V(obj, type); + switch (ct_type) { + + case 0: break; /* NOP */ + + case K_CONTROL_EDIT: + if (event.segment && ((GET_SEL32V(event, type)) == SCI_EVT_KEYBOARD)) { + int max_displayed = GET_SEL32V(obj, max); + int max = max_displayed; + int cursor = GET_SEL32V(obj, cursor); + int modifiers = GET_SEL32V(event, modifiers); + int key = GET_SEL32V(event, message); + reg_t text_pos = GET_SEL32(obj, text); + int display_offset = 0; + + char *text = (char *) sm_dereference(&s->seg_manager, text_pos, NULL); + int textlen; + + if (!text) { + SCIkdebug(SCIkWARNING, "Could not draw control: "PREG" does not reference text!\n", + PRINT_REG(text_pos)); + return s->r_acc; + } + + if (REG_EQ(text_pos, s->save_dir_copy)) { + max = MAX_SAVE_DIR_SIZE - 1; + display_offset = s->save_dir_edit_offset; + } + textlen = strlen(text); + + cursor += display_offset; + + if (cursor > textlen) + cursor = textlen; + + if (modifiers & SCI_EVM_CTRL) { + + switch (tolower((char)key)) { + case 'a': cursor = 0; break; + case 'e': cursor = textlen; break; + case 'f': if (cursor < textlen) ++cursor; break; + case 'b': if (cursor > 0) --cursor; break; + case 'k': text[cursor] = 0; break; /* Terminate string */ + case 'h': _K_EDIT_BACKSPACE; break; + case 'd': _K_EDIT_DELETE; break; + } + PUT_SEL32V(event, claimed, 1); + + } else if (modifiers & SCI_EVM_ALT) { /* Ctrl has precedence over Alt */ + + switch (key) { + case 0x2100 /* A-f */: while ((cursor < textlen) && (text[cursor++] != ' ')); break; + case 0x3000 /* A-b */: while ((cursor > 0) && (text[--cursor - 1] != ' ')); break; + case 0x2000 /* A-d */: { + + while ((cursor < textlen) && (text[cursor] == ' ')) { + _K_EDIT_DELETE; + textlen--; + } + + while ((cursor < textlen) && (text[cursor] != ' ')) { + _K_EDIT_DELETE; + textlen--; + } + break; + } + } + PUT_SEL32V(event, claimed, 1); + + } else if (key < 31) { + + PUT_SEL32V(event, claimed, 1); + + switch(key) { + case SCI_K_BACKSPACE: _K_EDIT_BACKSPACE; break; + default: + PUT_SEL32V(event, claimed, 0); + } + + } else if (key & 0xff00) { + + switch(key) { + case SCI_K_HOME: cursor = 0; break; + case SCI_K_END: cursor = textlen; break; + case SCI_K_RIGHT: if (cursor + 1 <= textlen) ++cursor; break; + case SCI_K_LEFT: if (cursor > 0) --cursor; break; + case SCI_K_DELETE: _K_EDIT_DELETE; break; + } + + PUT_SEL32V(event, claimed, 1); + } else if ((key > 31) && (key < 128)) { + int inserting = (modifiers & SCI_EVM_INSERT); + + modifiers &= ~(SCI_EVM_RSHIFT | SCI_EVM_LSHIFT | SCI_EVM_CAPSLOCK); + + if (cursor == textlen) { + if (textlen < max) { + text[cursor++] = key; + text[cursor] = 0; /* Terminate string */ + } + } else if (inserting) { + if (textlen < max) { + int i; + + for (i = textlen + 2; i >= cursor; i--) + text[i] = text[i - 1]; + text[cursor++] = key; + + } + } else { /* Overwriting */ + text[cursor++] = key; + } + + if (max_displayed < max) + update_cursor_limits(&display_offset, &cursor, max_displayed); + + if (REG_EQ(text_pos, s->save_dir_copy)) + s->save_dir_edit_offset = display_offset; + + cursor -= display_offset; + + PUT_SEL32V(event, claimed, 1); + } + + PUT_SEL32V(obj, cursor, cursor); /* Write back cursor position */ + } + + case K_CONTROL_ICON: + case K_CONTROL_BOX: + case K_CONTROL_BUTTON: + if (event.segment) PUT_SEL32V(event, claimed, 1); + _k_draw_control(s, obj, 0); + return NULL_REG; + break; + + case K_CONTROL_TEXT: { + int state = GET_SEL32V(obj, state); + PUT_SEL32V(obj, state, state | CONTROL_STATE_DITHER_FRAMED); + _k_draw_control(s, obj, 0); + PUT_SEL32V(obj, state, state); + } + break; + + default: + SCIkwarn(SCIkWARNING, "Attempt to edit control type %d\n", ct_type); + } + } + + return s->r_acc; +} + + +static void +_k_draw_control(state_t *s, reg_t obj, int inverse) +{ + int x = GET_SEL32SV(obj, nsLeft); + int y = GET_SEL32SV(obj, nsTop); + int xl = GET_SEL32SV(obj, nsRight) - x; + int yl = GET_SEL32SV(obj, nsBottom) - y; + rect_t area = gfx_rect(x, y, xl, yl); + + int font_nr = GET_SEL32V(obj, font); + reg_t text_pos = GET_SEL32(obj, text); + char *text = IS_NULL_REG(text_pos)? NULL : (char *) sm_dereference(&s->seg_manager, text_pos, NULL); + int view = GET_SEL32V(obj, view); + int cel = sign_extend_byte(GET_SEL32V(obj, cel)); + int loop = sign_extend_byte(GET_SEL32V(obj, loop)); + gfx_alignment_t mode; + + int type = GET_SEL32V(obj, type); + int state = GET_SEL32V(obj, state); + int cursor; + int max; + + if (REG_EQ(text_pos, s->save_dir_copy)) { + SCIkdebug(SCIkGRAPHICS, "Displaying the save_dir copy\n"); + } + + switch (type) { + + case K_CONTROL_BUTTON: + + SCIkdebug(SCIkGRAPHICS, "drawing button "PREG" to %d,%d\n", PRINT_REG(obj), x, y); + ADD_TO_CURRENT_BG_WIDGETS(sciw_new_button_control(s->port, obj, area, text, font_nr, + (gint8)(state & CONTROL_STATE_FRAMED), + (gint8)inverse, (gint8)(state & CONTROL_STATE_GRAY))); + break; + + case K_CONTROL_TEXT: + mode = (gfx_alignment_t) GET_SEL32V(obj, mode); + + SCIkdebug(SCIkGRAPHICS, "drawing text "PREG" to %d,%d, mode=%d\n", PRINT_REG(obj), x, y, mode); + + ADD_TO_CURRENT_BG_WIDGETS( + sciw_new_text_control(s->port, obj, area, text, font_nr, mode, + (gint8)(!!(state & CONTROL_STATE_DITHER_FRAMED)), + (gint8)inverse)); + break; + + case K_CONTROL_EDIT: + SCIkdebug(SCIkGRAPHICS, "drawing edit control "PREG" to %d,%d\n", PRINT_REG(obj), x, y); + + max = GET_SEL32V(obj, max); + cursor = GET_SEL32V(obj, cursor); + + if (cursor > (signed)strlen(text)) + cursor = strlen(text); + + if (REG_EQ(text_pos, s->save_dir_copy)) + update_cursor_limits(&s->save_dir_edit_offset, &cursor, max); + + update_cursor_limits(&s->save_dir_edit_offset, &cursor, max); + ADD_TO_CURRENT_BG_WIDGETS(sciw_new_edit_control(s->port, obj, area, text, font_nr, (unsigned)cursor, (gint8)inverse)); + break; + + case K_CONTROL_ICON: + + SCIkdebug(SCIkGRAPHICS, "drawing icon control "PREG" to %d,%d\n", PRINT_REG(obj), x, y -1); + + ADD_TO_CURRENT_BG_WIDGETS(sciw_new_icon_control(s->port, obj, area, view, loop, cel, + (gint8)(state & CONTROL_STATE_FRAMED), (gint8)inverse)); + break; + + case K_CONTROL_CONTROL: + case K_CONTROL_CONTROL_ALIAS: { + char **entries_list = NULL; + char *seeker; + int entries_nr; + int lsTop = GET_SEL32V(obj, lsTop)-text_pos.offset; + int list_top = 0; + int selection = 0; + int entry_size = GET_SEL32V(obj, x); + int i; + + SCIkdebug(SCIkGRAPHICS, "drawing list control %04x to %d,%d, diff %d\n", obj, x, y, + SCI_MAX_SAVENAME_LENGTH); + cursor = GET_SEL32V(obj, cursor) - text_pos.offset; + + entries_nr = 0; + seeker = text; + while (seeker[0]) { /* Count string entries in NULL terminated string list */ + ++entries_nr; + seeker += entry_size; + } + + if (entries_nr) { /* determine list_top, selection, and the entries_list */ + seeker = text; + entries_list = (char**)sci_malloc(sizeof(char *) * entries_nr); + for (i = 0; i < entries_nr; i++) { + entries_list[i] = seeker; + seeker += entry_size ; + if ((seeker - text) == lsTop) + list_top = i + 1; + if ((seeker - text) == cursor) + selection = i + 1; + } + } + + ADD_TO_CURRENT_BG_WIDGETS(sciw_new_list_control(s->port, obj, area, font_nr, entries_list, entries_nr, + list_top, selection, (gint8)inverse)); + if (entries_nr) + free(entries_list); + } + break; + + case K_CONTROL_BOX: + break; + + default: + SCIkwarn(SCIkWARNING, "Unknown control type: %d at "PREG", at (%d, %d) size %d x %d\n", + type, PRINT_REG(obj), x, y, xl, yl); + } + + if (!s->pic_not_valid) { + FULL_REDRAW(); + } +} + + +static void +draw_rect_to_control_map(state_t *s, abs_rect_t abs_zone) +{ + gfxw_box_t *box; + gfx_color_t color; + + gfxop_set_color(s->gfx_state, &color, -1, -1, -1, -1, -1, 0xf); + + SCIkdebug(SCIkGRAPHICS," adding control block (%d,%d)to(%d,%d)\n", + abs_zone.x, abs_zone.y, abs_zone.xend, abs_zone.yend); + + box = gfxw_new_box(s->gfx_state, + gfx_rect(abs_zone.x, abs_zone.y, + abs_zone.xend - abs_zone.x, + abs_zone.yend - abs_zone.y), + color, color, GFX_BOX_SHADE_FLAT); + + assert_primary_widget_lists(s); + + ADD_TO_CURRENT_PICTURE_PORT(box); +} + +static inline void +draw_obj_to_control_map(state_t *s, gfxw_dyn_view_t *view) +{ + reg_t obj = make_reg(view->ID, view->subID); + + if (!is_object(s, obj)) + SCIkwarn(SCIkWARNING, "View %d does not contain valid object reference "PREG"\n", view->ID, PRINT_REG(obj)); + + if (!(view->signalp && (((reg_t *)view->signalp)->offset & _K_VIEW_SIG_FLAG_IGNORE_ACTOR))) { + abs_rect_t abs_zone = get_nsrect(s, make_reg(view->ID, view->subID), 1); + draw_rect_to_control_map (s, abs_zone); + } +} + + +static void +_k_view_list_do_postdraw(state_t *s, gfxw_list_t *list) +{ + gfxw_dyn_view_t *widget = (gfxw_dyn_view_t *) list->contents; + + while (widget) { + reg_t obj = make_reg(widget->ID, widget->subID); + + if (widget->type == GFXW_SORTED_LIST) + _k_view_list_do_postdraw(s, GFXWC(widget)); + + if (widget->type != GFXW_DYN_VIEW) { + widget = (gfxw_dyn_view_t *) widget->next; + continue; + } + + /* + * this fixes a few problems, but doesn't match SSCI's logic. + * The semantics of the private flag need to be verified before this can be uncommented. + * Fixes bug #326 (CB1, ego falls down stairs) + * if ((widget->signal & (_K_VIEW_SIG_FLAG_FREESCI_PRIVATE | _K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_NO_UPDATE)) == _K_VIEW_SIG_FLAG_FREESCI_PRIVATE) { + */ + if ((widget->signal & (_K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_NO_UPDATE)) == 0) { + int has_nsrect = lookup_selector(s, obj, s->selector_map.nsBottom, NULL, NULL) == SELECTOR_VARIABLE; + + if (has_nsrect) { + int temp; + + temp = GET_SEL32V(obj, nsLeft); + PUT_SEL32V(obj, lsLeft, temp); + + temp = GET_SEL32V(obj, nsRight); + PUT_SEL32V(obj, lsRight, temp); + + temp = GET_SEL32V(obj, nsTop); + PUT_SEL32V(obj, lsTop, temp); + + temp = GET_SEL32V(obj, nsBottom); + PUT_SEL32V(obj, lsBottom, temp); +#ifdef DEBUG_LSRECT + fprintf(stderr, "lsRected "PREG"\n", PRINT_REG(obj)); +#endif + } +#ifdef DEBUG_LSRECT + else fprintf(stderr, "Not lsRecting "PREG" because %d\n", PRINT_REG(obj), + lookup_selector(s, obj, s->selector_map.nsBottom, NULL, NULL)); +#endif + + if (widget->signal & _K_VIEW_SIG_FLAG_HIDDEN) + widget->signal |= _K_VIEW_SIG_FLAG_REMOVE; + } +#ifdef DEBUG_LSRECT + fprintf(stderr, "obj "PREG" has pflags %x\n", PRINT_REG(obj), (widget->signal & (_K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_NO_UPDATE))); +#endif + + if (widget->signalp) { + *((reg_t *)(widget->signalp)) = make_reg(0, widget->signal & 0xffff); /* Write back signal */ + } + + widget = (gfxw_dyn_view_t *) widget->next; + } +} + +void +_k_view_list_mark_free(state_t *s, reg_t off) +{ + if (s->dyn_views) { + + gfxw_dyn_view_t *w = (gfxw_dyn_view_t *) s->dyn_views->contents; + + while (w) { + if (w->ID == off.segment + && w->subID == off.offset) { + w->under_bitsp = NULL; + } + + w = (gfxw_dyn_view_t *) w->next; + } + } +} + +static int _k_animate_ran = 0; + +int +_k_view_list_dispose_loop(state_t *s, list_t *list, gfxw_dyn_view_t *widget, + int funct_nr, int argc, reg_t *argv) + /* disposes all list members flagged for disposal; funct_nr is the invoking kfunction */ + /* returns non-zero IFF views were dropped */ +{ + int signal; + int dropped = 0; + + _k_animate_ran = 0; + + if (widget) { + int retval; + /* Recurse: */ + retval = _k_view_list_dispose_loop(s, list, (gfxw_dyn_view_t *) widget->next, funct_nr, argc, argv); + + if (retval == -1) /* Bail out on annihilation, rely on re-start from Animate() */ + return -1; + + if (GFXW_IS_DYN_VIEW(widget) && (widget->ID != GFXW_NO_ID)) { + signal = ((reg_t *)widget->signalp)->offset; + if (signal & _K_VIEW_SIG_FLAG_DISPOSE_ME) { + reg_t obj = make_reg(widget->ID, widget->subID); + reg_t under_bits = NULL_REG; + + if (!is_object(s, obj)) { + SCIkwarn(SCIkERROR, "Non-object "PREG" present" + " in view list during delete time\n", + PRINT_REG(obj)); + obj = NULL_REG; + } else + + if (widget->under_bitsp) { /* Is there a bg picture left to clean? */ + + reg_t mem_handle = *((reg_t*)(widget->under_bitsp)); + + if (mem_handle.segment) { + if (!kfree(s, mem_handle)) { + *((reg_t*)(widget->under_bitsp)) = make_reg(0, widget->under_bits = 0); + } else { + SCIkwarn(SCIkWARNING, + "Treating viewobj "PREG + " as no longer" + " present\n", PRINT_REG(obj)); + obj = NULL_REG; + } + } + } + + if (is_object(s, obj)) { + if (invoke_selector(INV_SEL(obj, delete, 1), 0)) + SCIkwarn(SCIkWARNING, "Object at "PREG" requested deletion, but does not have" + " a delete funcselector\n", PRINT_REG(obj)); + if (_k_animate_ran) { + SCIkwarn(SCIkWARNING, "Object at "PREG" invoked kAnimate() during deletion!\n", + PRINT_REG(obj)); + return dropped; + } + + if (widget->under_bitsp) + under_bits = *((reg_t*)(widget->under_bitsp)); + + if (under_bits.segment) { + *((reg_t*)(widget->under_bitsp)) = make_reg(0, 0); + graph_restore_box(s, under_bits); + } + + SCIkdebug(SCIkGRAPHICS, "Freeing "PREG" with signal=%04x\n", + PRINT_REG(obj), signal); + + if (!(signal & _K_VIEW_SIG_FLAG_HIDDEN)) { + SCIkdebug(SCIkGRAPHICS, "Adding view at "PREG" to background\n", + PRINT_REG(obj)); + if (!(gfxw_remove_id(widget->parent, widget->ID, widget->subID) == GFXW(widget))) { + SCIkwarn(SCIkERROR, "Attempt to remove view with ID %x:%x from list failed!\n", + widget->ID, widget->subID); + BREAKPOINT(); + } + + s->drop_views->add(GFXWC(s->drop_views), GFXW(gfxw_picviewize_dynview(widget))); + + draw_obj_to_control_map(s, widget); + widget->draw_bounds.y += s->dyn_views->bounds.y - widget->parent->bounds.y; + widget->draw_bounds.x += s->dyn_views->bounds.x - widget->parent->bounds.x; + dropped = 1; + } + else { + SCIkdebug(SCIkGRAPHICS, "Deleting view at "PREG"\n", PRINT_REG(obj)); + widget->flags |= GFXW_FLAG_VISIBLE; + gfxw_annihilate(GFXW(widget)); + return -1; /* restart: Done in Animate() */ + } + } + } + } + + } + + return dropped; +} + + +#define _K_MAKE_VIEW_LIST_CYCLE 1 +#define _K_MAKE_VIEW_LIST_CALC_PRIORITY 2 +#define _K_MAKE_VIEW_LIST_DRAW_TO_CONTROL_MAP 4 + +static gfxw_dyn_view_t * +_k_make_dynview_obj(state_t *s, reg_t obj, int options, int nr, int funct_nr, int argc, reg_t *argv) +{ + short oldloop, oldcel; + int cel, loop, view_nr = GET_SEL32SV(obj, view); + int palette; + int signal; + reg_t under_bits; + reg_t *under_bitsp, *signalp; + point_t pos; + int z; + gfxw_dyn_view_t *widget; + + SCIkdebug(SCIkGRAPHICS, " - Adding "PREG"\n", PRINT_REG(obj)); + + obj = obj; + + pos.x = GET_SEL32SV(obj, x); + pos.y = GET_SEL32SV(obj, y); + + pos.y++; /* magic: Sierra appears to do something like this */ + + z = GET_SEL32SV(obj, z); + + /* !-- nsRect used to be checked here! */ + loop = oldloop = sign_extend_byte(GET_SEL32V(obj, loop)); + cel = oldcel = sign_extend_byte(GET_SEL32V(obj, cel)); + + if (s->selector_map.palette) + palette = GET_SEL32V(obj, palette); else + palette = 0; + + /* Clip loop and cel, write back if neccessary */ + if (gfxop_check_cel(s->gfx_state, view_nr, &loop, &cel)) { + return NULL; + } + + if (loop != oldloop) + loop = 0; + if (cel != oldcel) + cel = 0; + + if (oldloop != loop) + PUT_SEL32V(obj, loop, loop); + + if (oldcel != cel) { + PUT_SEL32V(obj, cel, cel); + } + + if (lookup_selector(s, obj, s->selector_map.underBits, &(under_bitsp), NULL) + != SELECTOR_VARIABLE) { + under_bitsp = NULL; + under_bits = NULL_REG; + SCIkdebug(SCIkGRAPHICS, "Object at "PREG" has no underBits\n", PRINT_REG(obj)); + } else + under_bits = *((reg_t *)under_bitsp); + + if (lookup_selector(s, obj, s->selector_map.signal, &(signalp), NULL) + != SELECTOR_VARIABLE) { + signalp = NULL; + signal = 0; + SCIkdebug(SCIkGRAPHICS, "Object at "PREG" has no signal selector\n", PRINT_REG(obj)); + } else { + signal = signalp->offset; + SCIkdebug(SCIkGRAPHICS, " with signal = %04x\n", signal); + } + + widget = gfxw_new_dyn_view(s->gfx_state, pos, z, view_nr, loop, cel, palette, + -1, -1, ALIGN_CENTER, ALIGN_BOTTOM, nr); + + if (widget) { + + widget = (gfxw_dyn_view_t *) gfxw_set_id(GFXW(widget), obj.segment, obj.offset); + widget = gfxw_dyn_view_set_params(widget, under_bits.segment, + under_bitsp, signal, signalp); + widget->flags |= GFXW_FLAG_IMMUNE_TO_SNAPSHOTS; /* Only works the first time 'round */ + + return widget; + } else { + SCIkwarn(SCIkWARNING, "Could not generate dynview widget for %d/%d/%d\n", view_nr, loop, cel); + return NULL; + } +} + + +static void +_k_make_view_list(state_t *s, gfxw_list_t **widget_list, list_t *list, int options, + int funct_nr, int argc, reg_t *argv) + /* Creates a view_list from a node list in heap space. Returns the list, stores the + ** number of list entries in *list_nr. Calls doit for each entry if cycle is set. + ** argc, argv, funct_nr should be the same as in the calling kernel function. + */ +{ + node_t *node; + int sequence_nr = 0; + gfxw_dyn_view_t *widget; + + if (!*widget_list) { + SCIkwarn(SCIkERROR, "make_view_list with widget_list == ()\n"); + BREAKPOINT(); + }; + + assert_primary_widget_lists(s); + /* In case one of the views' doit() does a DrawPic... */ + /* Yes, this _does_ happen! */ + + if (!list) { /* list sanity check */ + SCIkwarn(SCIkERROR, "Attempt to make list from non-list!\n"); + BREAKPOINT(); + } + + node = LOOKUP_NODE(list->first); + while (node) { + reg_t obj = node->value; /* The object we're using */ + reg_t next_node; + gfxw_dyn_view_t *widget; + + if (options & _K_MAKE_VIEW_LIST_CYCLE) { + unsigned int signal = GET_SEL32V(obj, signal); + + if (!(signal & _K_VIEW_SIG_FLAG_FROZEN)) { + + SCIkdebug(SCIkGRAPHICS, " invoking "PREG"::doit()\n", PRINT_REG(obj)); + invoke_selector(INV_SEL(obj, doit, 1), 0); /* Call obj::doit() if neccessary */ + } + } + + next_node = node->succ; /* In case the cast list was changed */ + + if (list->first.segment == 0 && + list->first.offset == 0) /* The cast list was completely emptied! */ + break; + + widget = _k_make_dynview_obj(s, obj, options, sequence_nr--, + funct_nr, argc, argv); + if (widget) + GFX_ASSERT((*widget_list)->add(GFXWC(*widget_list), GFXW(widget))); + + node = LOOKUP_NODE(next_node); /* Next node */ + } + + + widget = (gfxw_dyn_view_t *) (*widget_list)->contents; + + while(widget) { /* Read back widget values */ + if (widget->signalp) + widget->signal = ((reg_t *)(widget->signalp))->offset; + + widget = (gfxw_dyn_view_t *) widget->next; + } +} + + +static void +_k_prepare_view_list(state_t *s, gfxw_list_t *list, int options) +{ + gfxw_dyn_view_t *view = (gfxw_dyn_view_t *) list->contents; + while (view) { + reg_t obj = make_reg(view->ID, view->subID); + int priority, _priority; + int has_nsrect = (view->ID <=0)? 0 : lookup_selector(s, obj, s->selector_map.nsBottom, NULL, NULL) == SELECTOR_VARIABLE; + int oldsignal = view->signal; + + _k_set_now_seen(s, obj); + _priority = /*GET_SELECTOR(obj, y); */((view->pos.y));/**/ + _priority = _find_view_priority(s, _priority - 1); + + if (options & _K_MAKE_VIEW_LIST_DRAW_TO_CONTROL_MAP) { /* Picview */ + priority = GET_SEL32SV(obj, priority); + if (priority < 0) + priority = _priority; /* Always for picviews */ + } else { /* Dynview */ + if (has_nsrect + && !(view->signal & _K_VIEW_SIG_FLAG_FIX_PRI_ON)) { /* Calculate priority */ + + if (options & _K_MAKE_VIEW_LIST_CALC_PRIORITY) + PUT_SEL32V(obj, priority, _priority); + + priority = _priority; + + } else /* DON'T calculate the priority */ + priority = GET_SEL32SV(obj, priority); + } + + + view->color.priority = priority; + + if (priority > -1) + view->color.mask |= GFX_MASK_PRIORITY; + else + view->color.mask &= ~GFX_MASK_PRIORITY; + + /* CR (from :Bob Heitman:) stopupdated views (like pic views) have + ** their clipped nsRect drawn to the control map */ + if (view->signal & _K_VIEW_SIG_FLAG_STOP_UPDATE) { + view->signal |= _K_VIEW_SIG_FLAG_FREESCI_STOPUPD; + SCIkdebug(SCIkGRAPHICS, "Setting magic STOP_UPD for "PREG"\n", PRINT_REG(obj)); + } + + if ((options & _K_MAKE_VIEW_LIST_DRAW_TO_CONTROL_MAP)) + draw_obj_to_control_map(s, view); + + + /* Extreme Pattern Matching ugliness ahead... */ + if (view->signal & _K_VIEW_SIG_FLAG_NO_UPDATE) { + if (((view->signal & (_K_VIEW_SIG_FLAG_UPDATED | _K_VIEW_SIG_FLAG_FORCE_UPDATE))) /* 9.1.1.1 */ + || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_REMOVE)) == _K_VIEW_SIG_FLAG_HIDDEN) + || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_REMOVE)) == _K_VIEW_SIG_FLAG_REMOVE) /* 9.1.1.2 */ + || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == _K_VIEW_SIG_FLAG_ALWAYS_UPDATE) /* 9.1.1.3 */ + || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE))) /* 9.1.1.4 */ + { + s->pic_not_valid++; + view->signal &= ~_K_VIEW_SIG_FLAG_STOP_UPDATE; + } + + else if (((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == 0) + || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_REMOVE)) + || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) + || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == _K_VIEW_SIG_FLAG_HIDDEN)) + { + view->signal &= ~_K_VIEW_SIG_FLAG_STOP_UPDATE; + } + } + else { + if (view->signal & _K_VIEW_SIG_FLAG_STOP_UPDATE) { + s->pic_not_valid++; + view->signal &= ~_K_VIEW_SIG_FLAG_FORCE_UPDATE; + } + else { /* if not STOP_UPDATE */ + if (view->signal & _K_VIEW_SIG_FLAG_ALWAYS_UPDATE) + s->pic_not_valid++; + view->signal &= ~_K_VIEW_SIG_FLAG_FORCE_UPDATE; + } + } + + SCIkdebug(SCIkGRAPHICS, " dv["PREG"]: signal %04x -> %04x\n", PRINT_REG(obj), oldsignal, view->signal); + + /* Never happens + if (view->signal & 0) { + view->signal &= ~_K_VIEW_SIG_FLAG_FREESCI_STOPUPD; + fprintf(stderr, "Unsetting magic StopUpd for view "PREG"\n", PRINT_REG(obj)); + } */ + + view = (gfxw_dyn_view_t *) view->next; + } +} + +static void +_k_update_signals_in_view_list(gfxw_list_t *old_list, gfxw_list_t *new_list) +{ /* O(n^2)... a bit painful, but much faster than the redraws it helps prevent */ + gfxw_dyn_view_t *old_widget = (gfxw_dyn_view_t *) old_list->contents; + + /* Traverses all old widgets, updates them with signals from the new widgets. + ** This is done to avoid evil hacks in widget.c; widgets with unique IDs are + ** replaced there iff they are NOT equal_to a new widget with the same ID. + ** If they were replaced every time, we'd be doing far too many redraws. + */ + + while (old_widget) { + gfxw_dyn_view_t *new_widget = (gfxw_dyn_view_t *) new_list->contents; + + while (new_widget + && (new_widget->ID != old_widget->ID + || new_widget->subID != old_widget->subID)) + new_widget = (gfxw_dyn_view_t *) new_widget->next; + + if (new_widget) { + int carry = old_widget->signal & _K_VIEW_SIG_FLAG_FREESCI_STOPUPD; + /* Transfer 'stopupd' flag */ + + if ((new_widget->pos.x != old_widget->pos.x) + || (new_widget->pos.y != old_widget->pos.y) + /* ** No idea why this is supposed to be bad ** + || (new_widget->z != old_widget->z) + || (new_widget->view != old_widget->view) + || (new_widget->loop != old_widget->loop) + || (new_widget->cel != old_widget->cel) + */ + ) + carry = 0; + + old_widget->signal = new_widget->signal |= carry; + } + + old_widget = (gfxw_dyn_view_t *) old_widget->next; + } +} + +static void +_k_view_list_kryptonize(gfxw_widget_t *v) +{ + if (v) { + v->flags &= ~GFXW_FLAG_IMMUNE_TO_SNAPSHOTS; + _k_view_list_kryptonize(v->next); + } +} + +static void +_k_raise_topmost_in_view_list(state_t *s, gfxw_list_t *list, gfxw_dyn_view_t *view) +{ + if (view) { + gfxw_dyn_view_t *next = (gfxw_dyn_view_t *) view->next; + + /* step 11 */ + if ((view->signal & (_K_VIEW_SIG_FLAG_NO_UPDATE | _K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == 0) { + SCIkdebug(SCIkGRAPHICS, "Forcing precedence 2 at ["PREG"] with %04x\n", PRINT_REG(make_reg(view->ID, view->subID)), view->signal); + view->force_precedence = 2; + + if ((view->signal & (_K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_HIDDEN)) == _K_VIEW_SIG_FLAG_REMOVE) { + view->signal &= ~_K_VIEW_SIG_FLAG_REMOVE; + } + } + + gfxw_remove_widget_from_container(view->parent, GFXW(view)); + + gfxw_widget_reparent_chrono(s->visual, GFXW(view), GFXWC(list)); + + if (view->signal & _K_VIEW_SIG_FLAG_HIDDEN) + gfxw_hide_widget(GFXW(view)); + else + gfxw_show_widget(GFXW(view)); + + list->add(GFXWC(list), GFXW(view)); + + _k_raise_topmost_in_view_list(s, list, next); + } +} + + +static void +_k_redraw_view_list(state_t *s, gfxw_list_t *list) +{ + gfxw_dyn_view_t *view = (gfxw_dyn_view_t *) list->contents; + while (view) { + + SCIkdebug(SCIkGRAPHICS, " dv["PREG"]: signal %04x\n", make_reg(view->ID, view->subID), view->signal); + + /* step 1 of subalgorithm */ + if (view->signal & _K_VIEW_SIG_FLAG_NO_UPDATE) { + if (view->signal & _K_VIEW_SIG_FLAG_FORCE_UPDATE) + view->signal &= ~_K_VIEW_SIG_FLAG_FORCE_UPDATE; + + if (view->signal & _K_VIEW_SIG_FLAG_UPDATED) + view->signal &= ~(_K_VIEW_SIG_FLAG_UPDATED | _K_VIEW_SIG_FLAG_NO_UPDATE); + } else { /* NO_UPD is not set */ + if (view->signal & _K_VIEW_SIG_FLAG_STOP_UPDATE) { + view->signal &= ~_K_VIEW_SIG_FLAG_STOP_UPDATE; + view->signal |= _K_VIEW_SIG_FLAG_NO_UPDATE; + } + } + + SCIkdebug(SCIkGRAPHICS, " at substep 6: signal %04x\n", view->signal); + + if (view->signal & _K_VIEW_SIG_FLAG_ALWAYS_UPDATE) + view->signal &= ~(_K_VIEW_SIG_FLAG_STOP_UPDATE | _K_VIEW_SIG_FLAG_UPDATED + | _K_VIEW_SIG_FLAG_NO_UPDATE | _K_VIEW_SIG_FLAG_FORCE_UPDATE); + + SCIkdebug(SCIkGRAPHICS, " at substep 11/14: signal %04x\n", view->signal); + + if (view->signal & _K_VIEW_SIG_FLAG_NO_UPDATE) { + if (view->signal & _K_VIEW_SIG_FLAG_HIDDEN) + view->signal |= _K_VIEW_SIG_FLAG_REMOVE; + else + view->signal &= ~_K_VIEW_SIG_FLAG_REMOVE; + } else if (!(view->signal & _K_VIEW_SIG_FLAG_HIDDEN)) + view->force_precedence = 1; + + SCIkdebug(SCIkGRAPHICS, " -> signal %04x\n", view->signal); + + view = (gfxw_dyn_view_t *) view->next; + } +} + + +/* Flags for _k_draw_view_list */ +/* Whether some magic with the base object's "signal" selector should be done: */ +#define _K_DRAW_VIEW_LIST_USE_SIGNAL 1 +/* This flag draws all views with the "DISPOSE_ME" flag set: */ +#define _K_DRAW_VIEW_LIST_DISPOSEABLE 2 +/* Use this one to draw all views with "DISPOSE_ME" NOT set: */ +#define _K_DRAW_VIEW_LIST_NONDISPOSEABLE 4 +/* Draw as picviews */ +#define _K_DRAW_VIEW_LIST_PICVIEW 8 + + +void +_k_draw_view_list(state_t *s, gfxw_list_t *list, int flags) + /* Draws list_nr members of list to s->pic. */ +{ + gfxw_dyn_view_t *widget = (gfxw_dyn_view_t *) list->contents; + + if (GFXWC(s->port) != GFXWC(s->dyn_views->parent)) + return; /* Return if the pictures are meant for a different port */ + + while (widget) { + + if (flags & _K_DRAW_VIEW_LIST_PICVIEW) + widget = gfxw_picviewize_dynview(widget); + + if (GFXW_IS_DYN_VIEW(widget) && widget->ID) { + word signal = (flags & _K_DRAW_VIEW_LIST_USE_SIGNAL)? ((reg_t *)(widget->signalp))->offset : 0; + + if (signal & _K_VIEW_SIG_FLAG_HIDDEN) + gfxw_hide_widget(GFXW(widget)); + else + gfxw_show_widget(GFXW(widget)); + + if (!(flags & _K_DRAW_VIEW_LIST_USE_SIGNAL) + || ((flags & _K_DRAW_VIEW_LIST_DISPOSEABLE) && (signal & _K_VIEW_SIG_FLAG_DISPOSE_ME)) + || ((flags & _K_DRAW_VIEW_LIST_NONDISPOSEABLE) && !(signal & _K_VIEW_SIG_FLAG_DISPOSE_ME))) { + + if (flags & _K_DRAW_VIEW_LIST_USE_SIGNAL) { + signal &= ~(_K_VIEW_SIG_FLAG_STOP_UPDATE | _K_VIEW_SIG_FLAG_UPDATED | + _K_VIEW_SIG_FLAG_NO_UPDATE | _K_VIEW_SIG_FLAG_FORCE_UPDATE); + /* Clear all of those flags */ + + if (signal & _K_VIEW_SIG_FLAG_HIDDEN) + gfxw_hide_widget(GFXW(widget)); + else + gfxw_show_widget(GFXW(widget)); + + *((reg_t *)(widget->signalp)) = make_reg(0, signal); /* Write the changes back */ + }; + + } /* ...if we're drawing disposeables and this one is disposeable, or if we're drawing non- + ** disposeables and this one isn't disposeable */ + } + + widget = (gfxw_dyn_view_t *) widget->next; + } /* while (widget) */ + +} + +reg_t +kAddToPic(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + gfxw_list_t *pic_views; + reg_t list_ref = argv[0]; + + assert_primary_widget_lists(s); + + if (argc > 1) { + int view, cel, loop, x, y, priority, control; + gfxw_widget_t *widget; + + view = KP_UINT(argv[0]); + loop = KP_UINT(argv[1]); + cel = KP_UINT(argv[2]); + x = KP_SINT(argv[3]); + y = KP_SINT(argv[4]) + 1 /* magic + 1 */; + priority = KP_SINT(argv[5]); + control = KP_SINT(argv[6]); + + widget = GFXW(gfxw_new_dyn_view(s->gfx_state, gfx_point(x, y), 0, view, loop, cel, 0, + priority, -1 /* No priority */ , ALIGN_CENTER, ALIGN_BOTTOM, 0)); + + if (!widget) { + SCIkwarn(SCIkERROR, "Attempt to single-add invalid picview (%d/%d/%d)\n", view, loop, cel); + } else { + widget->ID = -1; + if (control >= 0) { + abs_rect_t abs_zone = nsrect_clip(s, y, + calculate_nsrect(s, x, y, + view, loop, cel), + priority); + + draw_rect_to_control_map(s, abs_zone); + } + ADD_TO_CURRENT_PICTURE_PORT(gfxw_picviewize_dynview((gfxw_dyn_view_t *) widget)); + } + + } else { + list_t *list; + + if (!list_ref.segment) { + SCIkdebug(SCIkWARNING, "Attempt to AddToPic single non-list: "PREG"\n", + PRINT_REG(list_ref)); + return s->r_acc; + } + + + list = LOOKUP_LIST(list_ref); + + pic_views = gfxw_new_list(s->picture_port->bounds, 1); + + SCIkdebug(SCIkGRAPHICS, "Preparing picview list...\n"); + _k_make_view_list(s, &pic_views, list, 0, funct_nr, argc, argv); + _k_prepare_view_list(s, pic_views, _K_MAKE_VIEW_LIST_DRAW_TO_CONTROL_MAP); + /* Store pic views for later re-use */ + + SCIkdebug(SCIkGRAPHICS, "Drawing picview list...\n"); + ADD_TO_CURRENT_PICTURE_PORT(pic_views); + _k_draw_view_list(s, pic_views, _K_DRAW_VIEW_LIST_NONDISPOSEABLE | _K_DRAW_VIEW_LIST_DISPOSEABLE | _K_DRAW_VIEW_LIST_PICVIEW); + /* Draw relative to the bottom center */ + SCIkdebug(SCIkGRAPHICS, "Returning.\n"); + } + reparentize_primary_widget_lists(s, s->port); + + return s->r_acc; +} + + +reg_t +kGetPort(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + return make_reg(0, s->port->ID); +} + + +reg_t +kSetPort(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + if (activated_icon_bar && argc == 6) + { + port_origin_x = port_origin_y = 0; + activated_icon_bar = 0; + return s->r_acc; + } + + switch (argc) { + case 1 : { + unsigned int port_nr = SKPV(0); + gfxw_port_t *new_port; + + /* We depart from official semantics here, sorry! + Reasoning: Sierra SCI does not clip ports while we do. + Therefore a draw to the titlebar port (which is the + official semantics) would cut off the lower part of the + icons in an SCI1 icon bar. Instead we have an + iconbar_port that does not exist in SSCI. */ + if (port_nr == -1) port_nr = s->iconbar_port->ID; + + new_port = gfxw_find_port(s->visual, port_nr); + + if (!new_port) { + SCIkwarn(SCIkERROR, "Invalid port %04x requested\n", port_nr); + return NULL_REG; + } + + s->port->draw(GFXW(s->port), gfxw_point_zero); /* Update the port we're leaving */ + s->port = new_port; + return s->r_acc; + } + case 6 : { + port_origin_y = SKPV(0); + port_origin_x = SKPV(1); + + if (SKPV(0) == -10) + { + s->port->draw(GFXW(s->port), gfxw_point_zero); /* Update the port we're leaving */ + s->port = s->iconbar_port; + activated_icon_bar = 1; + return s->r_acc; + } + + s->gfx_state->options->pic_port_bounds = gfx_rect(UKPV(5), UKPV(4), + UKPV(3), UKPV(2)); + /* FIXME: Should really only invalidate all loaded pic resources here; + this is overkill */ + gfxr_free_all_resources(s->gfx_state->driver, s->gfx_state->resstate); + + break; + } + default : + SCIkwarn(SCIkERROR, "SetPort was called with %d parameters\n", argc); + break; + } + + return NULL_REG; +} + +static inline void +add_to_chrono(state_t *s, gfxw_widget_t *widget) +{ + gfxw_port_t *chrono_port; + gfxw_list_t *tw; + + chrono_port = gfxw_get_chrono_port(s->visual, &tw, 0); + tw->add(GFXWC(tw), widget); + + if (!chrono_port->parent) + ADD_TO_CURRENT_PORT(chrono_port); +} + +reg_t +kDrawCel(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int view = SKPV(0); + int loop = SKPV(1); + int cel = SKPV(2); + int x = SKPV(3); + int y = SKPV(4); + int priority = SKPV_OR_ALT(5, -1); + gfxw_view_t *new_view; + +/* + if (!view) { + SCIkwarn(SCIkERROR, "Attempt to draw non-existing view.%03d\n", view); + return; + } +*/ + + if (gfxop_check_cel(s->gfx_state, view, &loop, &cel)) { + SCIkwarn(SCIkERROR, "Attempt to draw non-existing view.%03d\n", view); + return s->r_acc; + } + + SCIkdebug(SCIkGRAPHICS, "DrawCel((%d,%d), (view.%d, %d, %d), p=%d)\n", x, y, view, loop, + cel, priority); + + new_view = gfxw_new_view(s->gfx_state, gfx_point(x, y), view, loop, cel, 0, priority, -1, + ALIGN_LEFT, ALIGN_TOP, GFXW_VIEW_FLAG_DONT_MODIFY_OFFSET); + +#if 0 + add_to_chrono(s, GFXW(new_view)); +#else + ADD_TO_CURRENT_PICTURE_PORT(GFXW(new_view)); +#endif + FULL_REDRAW(); + + + + return s->r_acc; +} + +reg_t +kDisposeWindow(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + unsigned int goner_nr = SKPV(0); + gfxw_port_t *goner; + gfxw_port_t *pred; + int id = s->visual->port_refs_nr; + + gfxw_widget_kill_chrono(s->visual, goner_nr); + goner = gfxw_find_port(s->visual, goner_nr); + if ((goner_nr < 3) || (goner == NULL)) { + SCIkwarn(SCIkERROR, "Removal of invalid window %04x requested\n", goner_nr); + return s->r_acc; + } + + if (s->dyn_views && GFXWC(s->dyn_views->parent) == GFXWC(goner)) { + reparentize_primary_widget_lists(s, (gfxw_port_t *) goner->parent); + } + + if (s->drop_views && GFXWC(s->drop_views->parent) == GFXWC(goner)) + s->drop_views = NULL; /* Kill it */ + + pred = gfxw_remove_port(s->visual, goner); + + if (goner == s->port) /* Did we kill the active port? */ + s->port = pred; + +/* Find the last port that exists and that isn't marked no-switch */ + while ((!s->visual->port_refs[id] && id >= 0) || + (s->visual->port_refs[id]->flags & GFXW_FLAG_NO_IMPLICIT_SWITCH)) + id--; + + sciprintf("Activating port %d after disposing window %d\n", id, goner_nr); + s->port = s->visual->port_refs[id]; + + if (!s->port) + s->port = gfxw_find_default_port(s->visual); + + gfxop_update(s->gfx_state); + return s->r_acc; +} + +reg_t +kNewWindow(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + gfxw_port_t *window; + int x, y, xl, yl, flags; + gfx_color_t bgcolor; + gfx_color_t fgcolor; + gfx_color_t black; + gfx_color_t white; + int priority; + int argextra = argc == 13 ? 4 : 0; /* Triggers in PQ3 */ + + y = SKPV(0); + x = SKPV(1); + yl = SKPV(2) - y; + xl = SKPV(3) - x; + + y += s->wm_port->bounds.y; + + if (x+xl > 319) + x -= ((x+xl) - 319); + + flags = SKPV(5+argextra); + + priority = SKPV_OR_ALT(6+argextra, -1); + bgcolor.mask = 0; + + if (SKPV_OR_ALT(8+argextra, 255) >= 0) { + if (s->resmgr->sci_version < SCI_VERSION_01_VGA) + bgcolor.visual = *(get_pic_color(s, SKPV_OR_ALT(8+argextra, 15))); + else + bgcolor.visual = *(get_pic_color(s, SKPV_OR_ALT(8+argextra, 255))); + bgcolor.mask = GFX_MASK_VISUAL; + } + + bgcolor.priority = priority; + bgcolor.mask |= priority >= 0 ? GFX_MASK_PRIORITY : 0; + bgcolor.alpha = 0; + SCIkdebug(SCIkGRAPHICS, "New window with params %d, %d, %d, %d\n", SKPV(0), SKPV(1), SKPV(2), SKPV(3)); + + fgcolor.visual = *(get_pic_color(s, SKPV_OR_ALT(7+argextra, 0))); + fgcolor.mask = GFX_MASK_VISUAL; + fgcolor.alpha = 0; + black.visual = *(get_pic_color(s, 0)); + black.mask = GFX_MASK_VISUAL; + black.alpha = 0; + white.visual = *(get_pic_color(s, s->resmgr->sci_version < SCI_VERSION_01_VGA ? 15 : 255)), + white.mask = GFX_MASK_VISUAL; + white.alpha = 0; + + window = sciw_new_window(s, gfx_rect(x, y, xl, yl), s->titlebar_port->font_nr, + fgcolor, bgcolor, s->titlebar_port->font_nr, + white, + black, + argv[4+argextra].segment ? kernel_dereference_char_pointer(s, argv[4+argextra], 0) : NULL, + flags); + + /* PQ3 has the interpreter store underBits implicitly. + The feature was promptly removed after its release, never to be seen again. */ + if (argextra) + gfxw_port_auto_restore_background(s->visual, window, + gfx_rect(SKPV(5), SKPV(4), + SKPV(7)-SKPV(5), SKPV(6)-SKPV(4))); + + ADD_TO_WINDOW_PORT(window); + FULL_REDRAW(); + + window->draw(GFXW(window), gfxw_point_zero); + gfxop_update(s->gfx_state); + + s->port = window; /* Set active port */ + + return make_reg(0, window->ID); +} + + +#define K_ANIMATE_CENTER_OPEN_H 0 /* horizontally open from center */ +#define K_ANIMATE_CENTER_OPEN_V 1 /* vertically open from center */ +#define K_ANIMATE_RIGHT_OPEN 2 /* open from right */ +#define K_ANIMATE_LEFT_OPEN 3 /* open from left */ +#define K_ANIMATE_BOTTOM_OPEN 4 /* open from bottom */ +#define K_ANIMATE_TOP_OPEN 5 /* open from top */ +#define K_ANIMATE_BORDER_OPEN_F 6 /* open from edges to center */ +#define K_ANIMATE_CENTER_OPEN_F 7 /* open from center to edges */ +#define K_ANIMATE_OPEN_CHECKERS 8 /* open random checkboard */ +#define K_ANIMATE_BORDER_CLOSE_H_CENTER_OPEN_H 9 /* horizontally close to center,reopen from center */ +#define K_ANIMATE_BORDER_CLOSE_V_CENTER_OPEN_V 10 /* vertically close to center, reopen from center */ +#define K_ANIMATE_LEFT_CLOSE_RIGHT_OPEN 11 /* close to right, reopen from right */ +#define K_ANIMATE_RIGHT_CLOSE_LEFT_OPEN 12 /* close to left, reopen from left */ +#define K_ANIMATE_TOP_CLOSE_BOTTOM_OPEN 13 /* close to bottom, reopen from bottom */ +#define K_ANIMATE_BOTTOM_CLOSE_TOP_OPEN 14 /* close to top, reopen from top */ +#define K_ANIMATE_CENTER_CLOSE_F_BORDER_OPEN_F 15 /* close from center to edges, + ** reopen from edges to center */ +#define K_ANIMATE_BORDER_CLOSE_F_CENTER_OPEN_F 16 /* close from edges to center, reopen from + ** center to edges */ +#define K_ANIMATE_CLOSE_CHECKERS_OPEN_CHECKERS 17 /* close random checkboard, reopen */ +#define K_ANIMATE_SCROLL_LEFT 0x28 +#define K_ANIMATE_SCROLL_RIGHT 0x29 +#define K_ANIMATE_SCROLL_DOWN 0x2a +#define K_ANIMATE_SCROLL_UP 0x2b + +#define K_ANIMATE_OPEN_SIMPLE 100 /* No animation */ + + +#define GRAPH_BLANK_BOX(s, x, y, xl, yl, color) GFX_ASSERT(gfxop_fill_box(s->gfx_state, \ + gfx_rect(x, (((y) < 10)? 10 : (y)), xl, (((y) < 10)? ((y) - 10) : 0) + (yl)), s->ega_colors[color])); + +#define GRAPH_UPDATE_BOX(s, x, y, xl, yl) GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, newscreen, \ + gfx_rect(x, (((y) < 10)? 10 : (y)) - 10, xl, (((y) < 10)? ((y) - 10) : 0) + (yl)), gfx_point(x, ((y) < 10)? 10 : (y) ))); + + +static void +animate_do_animation(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int i, remaining_checkers; + int update_counter; + int granularity0 = s->animation_granularity << 1; + int granularity1 = s->animation_granularity; + int granularity2 = s->animation_granularity >> 2; + int granularity3 = s->animation_granularity >> 4; + char checkers[32 * 19]; + gfx_pixmap_t *newscreen = gfxop_grab_pixmap(s->gfx_state, gfx_rect(0, 10, 320, 190)); + + if (!granularity2) + granularity2 = 1; + if (!granularity3) + granularity3 = 1; + + gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen); + + if (!newscreen) { + SCIkwarn(SCIkERROR, "Failed to allocate 'newscreen'!\n"); + return; + } + + GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, s->old_screen, gfx_rect(0, 0, 320, 190), gfx_point(0, 10))); + gfxop_update_box(s->gfx_state, gfx_rect(0, 0, 320, 200)); + + /*SCIkdebug(SCIkGRAPHICS, "Animating pic opening type %x\n", s->pic_animate);*/ + + gfxop_enable_dirty_frames(s->gfx_state); + + if (s->animation_delay < 1) + s->pic_animate = K_ANIMATE_OPEN_SIMPLE; + + + switch(s->pic_animate) { + case K_ANIMATE_BORDER_CLOSE_H_CENTER_OPEN_H : + + for (i = 0; i < 159 + granularity1; i += granularity1) { + GRAPH_BLANK_BOX(s, i, 10, granularity1, 190, 0); + gfxop_update(s->gfx_state); + GRAPH_BLANK_BOX(s, 319-i, 10, granularity1, 190, 0); + gfxop_update(s->gfx_state); + gfxop_usleep(s->gfx_state, s->animation_delay); + process_sound_events(s); + } + GRAPH_BLANK_BOX(s, 0, 10, 320, 190, 0); + + case K_ANIMATE_CENTER_OPEN_H : + + for (i = 159; i >= 1-granularity1; i -= granularity1) { + GRAPH_UPDATE_BOX(s, i, 10, granularity1, 190); + gfxop_update(s->gfx_state); + GRAPH_UPDATE_BOX(s, 319-i, 10, granularity1, 190); + gfxop_update(s->gfx_state); + gfxop_usleep(s->gfx_state, s->animation_delay); + process_sound_events(s); + } + break; + + + case K_ANIMATE_BORDER_CLOSE_V_CENTER_OPEN_V : + + for (i = 0; i < 94 + granularity2; i += granularity2) { + GRAPH_BLANK_BOX(s, 0, i + 10, 320, granularity2, 0); + gfxop_update(s->gfx_state); + GRAPH_BLANK_BOX(s, 0, 199 - i, 320, granularity2, 0); + gfxop_update(s->gfx_state); + gfxop_usleep(s->gfx_state, 2 * s->animation_delay); + process_sound_events(s); + } + GRAPH_BLANK_BOX(s, 0, 10, 320, 190, 0); + + case K_ANIMATE_CENTER_OPEN_V : + + for (i = 94; i >= 1 - granularity2; i -= granularity2) { + GRAPH_UPDATE_BOX(s, 0, i + 10, 320, granularity2); + gfxop_update(s->gfx_state); + GRAPH_UPDATE_BOX(s, 0, 199 - i, 320, granularity2); + gfxop_update(s->gfx_state); + gfxop_usleep(s->gfx_state, 2 * s->animation_delay); + process_sound_events(s); + } + break; + + + case K_ANIMATE_LEFT_CLOSE_RIGHT_OPEN : + + for(i = 0; i < 319 + granularity0; i += granularity0) { + GRAPH_BLANK_BOX(s, i, 10, granularity0, 190, 0); + gfxop_update(s->gfx_state); + gfxop_usleep(s->gfx_state, s->animation_delay / 2); + process_sound_events(s); + } + GRAPH_BLANK_BOX(s, 0, 10, 320, 190, 0); + + case K_ANIMATE_RIGHT_OPEN : + for(i = 319; i >= 1 - granularity0; i -= granularity0) { + GRAPH_UPDATE_BOX(s, i, 10, granularity0, 190); + gfxop_update(s->gfx_state); + gfxop_usleep(s->gfx_state, s->animation_delay / 2); + process_sound_events(s); + } + break; + + + case K_ANIMATE_RIGHT_CLOSE_LEFT_OPEN : + + for(i = 319; i >= 1-granularity0; i -= granularity0) { + GRAPH_BLANK_BOX(s, i, 10, granularity0, 190, 0); + gfxop_update(s->gfx_state); + gfxop_usleep(s->gfx_state, s->animation_delay / 2); + process_sound_events(s); + } + GRAPH_BLANK_BOX(s, 0, 10, 320, 190, 0); + + case K_ANIMATE_LEFT_OPEN : + + for(i = 0; i < 319 + granularity0; i+= granularity0) { + GRAPH_UPDATE_BOX(s, i, 10, granularity0, 190); + gfxop_update(s->gfx_state); + gfxop_usleep(s->gfx_state, s->animation_delay / 2); + process_sound_events(s); + } + break; + + + case K_ANIMATE_TOP_CLOSE_BOTTOM_OPEN : + + for (i = 10; i < 199 + granularity1; i += granularity1) { + GRAPH_BLANK_BOX(s, 0, i, 320, granularity1, 0); + gfxop_update(s->gfx_state); + gfxop_usleep(s->gfx_state, s->animation_delay); + process_sound_events(s); + } + GRAPH_BLANK_BOX(s, 0, 10, 320, 190, 0); + + case K_ANIMATE_BOTTOM_OPEN : + + for (i = 199; i >= 11 - granularity1; i-= granularity1) { + GRAPH_UPDATE_BOX(s, 0, i, 320, granularity1); + gfxop_update(s->gfx_state); + gfxop_usleep(s->gfx_state, s->animation_delay); + process_sound_events(s); + } + break; + + + case K_ANIMATE_BOTTOM_CLOSE_TOP_OPEN : + + for (i = 199; i >= 11 - granularity1; i-= granularity1) { + GRAPH_BLANK_BOX(s, 0, i, 320, granularity1, 0); + gfxop_update(s->gfx_state); + gfxop_usleep(s->gfx_state, s->animation_delay); + process_sound_events(s); + } + GRAPH_BLANK_BOX(s, 0, 10, 320, 190, 0); + + case K_ANIMATE_TOP_OPEN : + + for (i = 10; i < 199 + granularity1; i+= granularity1) { + GRAPH_UPDATE_BOX(s, 0, i, 320, granularity1); + gfxop_update(s->gfx_state); + gfxop_usleep(s->gfx_state, s->animation_delay); + process_sound_events(s); + } + break; + + + case K_ANIMATE_CENTER_CLOSE_F_BORDER_OPEN_F : + + for (i = 31; i >= 1-granularity3; i -= granularity3) { + int real_i = (i < 0)? 0 : i; + int height_l = 3 * (granularity3 - real_i + i); + int width_l = 5 * (granularity3 - real_i + i); + int height = real_i * 3; + int width = real_i * 5; + + GRAPH_BLANK_BOX(s, width, 10 + height, + width_l, 190 - 2*height, 0); + gfxop_update(s->gfx_state); + GRAPH_BLANK_BOX(s, 320 - width_l - width, + 10 + height, width_l, 190 - 2*height, 0); + gfxop_update(s->gfx_state); + + GRAPH_BLANK_BOX(s, width, 10 + height, + 320 - 2*width, height_l, 0); + gfxop_update(s->gfx_state); + GRAPH_BLANK_BOX(s, width, 200 - height_l - height, + 320 - 2*width, height_l, 0); + gfxop_update(s->gfx_state); + + gfxop_usleep(s->gfx_state, 4 * s->animation_delay); + process_sound_events(s); + } + + + case K_ANIMATE_BORDER_OPEN_F : + + for (i = 0; i < 31+granularity3; i += granularity3) { + int real_i = (i < 0)? 0 : i; + int height_l = 3 * (granularity3 - real_i + i); + int width_l = 5 * (granularity3 - real_i + i); + int height = real_i * 3; + int width = real_i * 5; + + GRAPH_UPDATE_BOX(s, width, 10 + height, + width_l, 190 - 2*height); + gfxop_update(s->gfx_state); + GRAPH_UPDATE_BOX(s, 320 - width_l - width, + 10 + height, width_l, 190 - 2*height); + gfxop_update(s->gfx_state); + + GRAPH_UPDATE_BOX(s, width, 10 + height, + 320 - 2*width, height_l); + gfxop_update(s->gfx_state); + GRAPH_UPDATE_BOX(s, width, 200 - height_l - height, + 320 - 2*width, height_l); + gfxop_update(s->gfx_state); + + gfxop_usleep(s->gfx_state, 4 * s->animation_delay); + process_sound_events(s); + } + + break; + + + case K_ANIMATE_BORDER_CLOSE_F_CENTER_OPEN_F : + + for (i = 0; i < 31+granularity3; i += granularity3) { + int real_i = (i < 0)? 0 : i; + int height_l = 3 * (granularity3 - real_i + i); + int width_l = 5 * (granularity3 - real_i + i); + int height = real_i * 3; + int width = real_i * 5; + + GRAPH_BLANK_BOX(s, width, 10 + height, + width_l, 190 - 2*height, 0); + gfxop_update(s->gfx_state); + GRAPH_BLANK_BOX(s, 320 - width_l - width, + 10 + height, width_l, 190 - 2*height, 0); + gfxop_update(s->gfx_state); + + GRAPH_BLANK_BOX(s, width, 10 + height, + 320 - 2*width, height_l, 0); + gfxop_update(s->gfx_state); + GRAPH_BLANK_BOX(s, width, 200 - height_l - height, + 320 - 2*width, height_l, 0); + gfxop_update(s->gfx_state); + + gfxop_usleep(s->gfx_state, 7 * s->animation_delay); + process_sound_events(s); + } + + + case K_ANIMATE_CENTER_OPEN_F : + + for (i = 31; i >= 1-granularity3; i -= granularity3) { + int real_i = (i < 0)? 0 : i; + int height_l = 3 * (granularity3 - real_i + i); + int width_l = 5 * (granularity3 - real_i + i); + int height = real_i * 3; + int width = real_i * 5; + + GRAPH_UPDATE_BOX(s, width, 10 + height, + width_l, 190 - 2*height); + gfxop_update(s->gfx_state); + GRAPH_UPDATE_BOX(s, 320 - width_l - width, + 10 + height, width_l, 190 - 2*height); + gfxop_update(s->gfx_state); + + GRAPH_UPDATE_BOX(s, width, 10 + height, + 320 - 2 * width, height_l); + gfxop_update(s->gfx_state); + GRAPH_UPDATE_BOX(s, width, 200 - height_l - height, + 320 - 2 * width, height_l); + gfxop_update(s->gfx_state); + + gfxop_usleep(s->gfx_state, 7 * s->animation_delay); + process_sound_events(s); + } + + break; + + + case K_ANIMATE_CLOSE_CHECKERS_OPEN_CHECKERS : + + memset(checkers, 0, sizeof(checkers)); + remaining_checkers = 19 * 32; + update_counter = granularity1; + + while (remaining_checkers) { + int x, y, checker = 1 + (int) (1.0 * remaining_checkers*rand()/(RAND_MAX+1.0)); + i = -1; + + while (checker) + if (checkers[++i] == 0) --checker; + checkers[i] = 1; /* Mark checker as used */ + + x = i % 32; + y = i / 32; + + GRAPH_BLANK_BOX(s, x * 10, 10 + y * 10, 10, 10, 0); + if (!(update_counter--) || (remaining_checkers == 1)) { + gfxop_update(s->gfx_state); + update_counter = granularity1; + } + + if (remaining_checkers & 1) { + gfxop_usleep(s->gfx_state, s->animation_delay / 4); + } + + --remaining_checkers; + process_sound_events(s); + } + + case K_ANIMATE_OPEN_CHECKERS : + + memset(checkers, 0, sizeof(checkers)); + remaining_checkers = 19 * 32; + update_counter = granularity1; + + while (remaining_checkers) { + int x, y, checker = 1 + (int) (1.0 * remaining_checkers * rand()/(RAND_MAX+1.0)); + i = -1; + + while (checker) + if (checkers[++i] == 0) --checker; + checkers[i] = 1; /* Mark checker as used */ + + x = i % 32; + y = i / 32; + + GRAPH_UPDATE_BOX(s, x * 10, 10 + y * 10, 10, 10); + + if (!(update_counter--) || (remaining_checkers == 1)) { + gfxop_update(s->gfx_state); + update_counter = granularity1; + } + + if (remaining_checkers & 1) { + gfxop_usleep(s->gfx_state, s->animation_delay / 4); + } + + --remaining_checkers; + process_sound_events(s); + } + break; + + + case K_ANIMATE_SCROLL_LEFT : + + for (i = 0; i < 319; i += granularity0) { + GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, newscreen, + gfx_rect(320 - i, 0, i, 190), + gfx_point(0, 10))); + GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, s->old_screen, + gfx_rect(0, 0, 320 - i, 190), + gfx_point(i, 10))); + gfxop_update(s->gfx_state); + + gfxop_usleep(s->gfx_state, s->animation_delay >> 3); + } + GRAPH_UPDATE_BOX(s, 0, 10, 320, 190); + break; + + case K_ANIMATE_SCROLL_RIGHT : + + for (i = 0; i < 319; i += granularity0) { + GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, newscreen, + gfx_rect(0, 0, i, 190), + gfx_point(319-i, 10))); + GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, s->old_screen, + gfx_rect(i, 0, 320 - i, 190), + gfx_point(0, 10))); + gfxop_update(s->gfx_state); + + gfxop_usleep(s->gfx_state, s->animation_delay >> 3); + } + GRAPH_UPDATE_BOX(s, 0, 10, 320, 190); + break; + + case K_ANIMATE_SCROLL_UP : + + for (i = 0; i < 189; i += granularity0) { + GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, newscreen, + gfx_rect(0, 190 - i, 320, i), + gfx_point(0, 10))); + GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, s->old_screen, + gfx_rect(0, 0, 320, 190 - i), + gfx_point(0, 10 + i))); + gfxop_update(s->gfx_state); + + gfxop_usleep(s->gfx_state, s->animation_delay >> 3); + } + GRAPH_UPDATE_BOX(s, 0, 10, 320, 190); + break; + + case K_ANIMATE_SCROLL_DOWN : + + for (i = 0; i < 189; i += granularity0) { + GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, newscreen, + gfx_rect(0, 0, 320, i), + gfx_point(0, 200 - i))); + GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, s->old_screen, + gfx_rect(0, i, 320, 190 - i), + gfx_point(0, 10))); + gfxop_update(s->gfx_state); + + gfxop_usleep(s->gfx_state, s->animation_delay >> 3); + } + GRAPH_UPDATE_BOX(s, 0, 10, 320, 190); + break; + + default: + if (s->pic_animate != K_ANIMATE_OPEN_SIMPLE) + SCIkwarn(SCIkWARNING, "Unknown opening animation 0x%02x\n", s->pic_animate); + GRAPH_UPDATE_BOX(s, 0, 10, 320, 190); + + } + + GFX_ASSERT(gfxop_free_pixmap(s->gfx_state, s->old_screen)); + GFX_ASSERT(gfxop_free_pixmap(s->gfx_state, newscreen)); + s->old_screen = NULL; + +} + + +reg_t +kAnimate(state_t *s, int funct_nr, int argc, reg_t *argv) + /* Animations are supposed to take a maximum of s->animation_delay milliseconds. */ +{ + reg_t cast_list_ref = KP_ALT(0, NULL_REG); + int cycle = (KP_ALT(1, NULL_REG)).offset; + list_t *cast_list = NULL; + int open_animation = 0; + + + process_sound_events(s); /* Take care of incoming events (kAnimate is called semi-regularly) */ + _k_animate_ran = 1; /* Used by some of the invoked functions to check for recursion, which may, + ** after all, damage the cast list */ + + if (cast_list_ref.segment) { + cast_list = LOOKUP_LIST(cast_list_ref); + if (!cast_list) + return s->r_acc; + } + + + open_animation = (s->pic_is_new) && (s->pic_not_valid); + s->pic_is_new = 0; + + assert_primary_widget_lists(s); + + if (!s->dyn_views->contents /* Only reparentize empty dynview list */ + && ((GFXWC(s->port) != GFXWC(s->dyn_views->parent)) /* If dynviews are on other port... */ + || (s->dyn_views->next))) /* ... or not on top of the view list */ + reparentize_primary_widget_lists(s, s->port); + + if (cast_list) { + gfxw_list_t *templist = gfxw_new_list(s->dyn_views->bounds, 0); + + _k_make_view_list(s, &(templist), cast_list, (cycle? _K_MAKE_VIEW_LIST_CYCLE : 0) + | _K_MAKE_VIEW_LIST_CALC_PRIORITY, funct_nr, argc, argv); + + /* Make sure that none of the doits() did something evil */ + assert_primary_widget_lists(s); + + if (!s->dyn_views->contents /* Only reparentize empty dynview list */ + && ((GFXWC(s->port) != GFXWC(s->dyn_views->parent)) /* If dynviews are on other port... */ + || (s->dyn_views->next))) /* ... or not on top of the view list */ + reparentize_primary_widget_lists(s, s->port); + /* End of doit() recovery code */ + + + if (s->pic_is_new) { /* Happens if DrawPic() is executed by a dynview (yes, that happens) */ + kAnimate(s, funct_nr, argc, argv); /* Tail-recurse */ + return s->r_acc; + } + + SCIkdebug(SCIkGRAPHICS, "Handling Dynviews (..step 9 inclusive):\n"); + _k_prepare_view_list(s, templist, _K_MAKE_VIEW_LIST_CALC_PRIORITY); + + if (s->pic_not_valid) { + SCIkdebug(SCIkGRAPHICS, "PicNotValid=%d -> Subalgorithm:\n"); + _k_redraw_view_list(s, templist); + } + + _k_update_signals_in_view_list(s->dyn_views, templist); + s->dyn_views->tag(GFXW(s->dyn_views)); + + _k_raise_topmost_in_view_list(s, s->dyn_views, (gfxw_dyn_view_t *) templist->contents); + + templist->widfree(GFXW(templist)); + s->dyn_views->free_tagged(GFXWC(s->dyn_views)); /* Free obsolete dynviews */ + } /* if (cast_list) */ + + if (open_animation) { + gfxop_clear_box(s->gfx_state, gfx_rect(0, 10, 320, 190)); /* Propagate pic */ + s->visual->add_dirty_abs(GFXWC(s->visual), gfx_rect_fullscreen, 0); + /* Mark screen as dirty so picviews will be drawn correctly */ + FULL_REDRAW(); + + animate_do_animation(s, funct_nr, argc, argv); + } /* if (open_animation) */ + + if (cast_list) { + int retval; + int reparentize = 0; + + s->pic_not_valid = 0; + + _k_view_list_do_postdraw(s, s->dyn_views); + + /* _k_view_list_dispose_loop() returns -1 if it requested a re-start, so we do just that. */ + while ((retval = _k_view_list_dispose_loop(s, cast_list, (gfxw_dyn_view_t *) s->dyn_views->contents, funct_nr, argc, argv) < 0)) + reparentize = 1; + + if (s->drop_views->contents) { + s->drop_views = gfxw_new_list(s->dyn_views->bounds, GFXW_LIST_SORTED); + s->drop_views->flags |= GFXW_FLAG_IMMUNE_TO_SNAPSHOTS; + ADD_TO_CURRENT_PICTURE_PORT(s->drop_views); + } else { + assert(s->drop_views); + gfxw_remove_widget_from_container(s->drop_views->parent, GFXW(s->drop_views)); + ADD_TO_CURRENT_PICTURE_PORT(s->drop_views); + } + + if ((reparentize | retval) + && (GFXWC(s->port) == GFXWC(s->dyn_views->parent)) /* If dynviews are on the same port... */ + && (s->dyn_views->next)) /* ... and not on top of the view list... */ + reparentize_primary_widget_lists(s, s->port); /* ...then reparentize. */ + + _k_view_list_kryptonize(s->dyn_views->contents); + } + + FULL_REDRAW(); + return s->r_acc; +} + +#define SHAKE_DOWN 1 +#define SHAKE_RIGHT 2 + +reg_t +kShakeScreen(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int shakes = SKPV_OR_ALT(0, 1); + int directions = SKPV_OR_ALT(1, 1); + gfx_pixmap_t *screen = gfxop_grab_pixmap(s->gfx_state, gfx_rect(0, 0, 320, 200)); + int i; + + if (directions & ~3) + SCIkdebug(SCIkGRAPHICS, "ShakeScreen(): Direction bits are %x (unknown)\n", directions); + + gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen); + + for (i = 0; i < shakes; i++) { + int shake_down = (directions & SHAKE_DOWN)? 10 : 0; + int shake_right = (directions & SHAKE_RIGHT)? 10 : 0; + + if (directions & SHAKE_DOWN) + gfxop_draw_box(s->gfx_state, gfx_rect(0, 0, 320, 10), s->ega_colors[0], s->ega_colors[0], GFX_BOX_SHADE_FLAT); + + if (directions & SHAKE_RIGHT) + gfxop_draw_box(s->gfx_state, gfx_rect(0, 0, 10, 200), s->ega_colors[0], s->ega_colors[0], GFX_BOX_SHADE_FLAT); + + gfxop_draw_pixmap(s->gfx_state, screen, gfx_rect(0, 0, 320 - shake_right, 200 - shake_down), + gfx_point(shake_right, shake_down)); + + gfxop_update(s->gfx_state); + gfxop_usleep(s->gfx_state, 50000); + + + gfxop_draw_pixmap(s->gfx_state, screen, gfx_rect(0, 0, 320, 200), gfx_point(0, 0)); + gfxop_update(s->gfx_state); + gfxop_usleep(s->gfx_state, 50000); + } + + gfxop_free_pixmap(s->gfx_state, screen); + gfxop_update(s->gfx_state); + return s->r_acc; +} + +#define K_DISPLAY_SET_COORDS 100 +#define K_DISPLAY_SET_ALIGNMENT 101 +#define K_DISPLAY_SET_COLOR 102 +#define K_DISPLAY_SET_BGCOLOR 103 +#define K_DISPLAY_SET_GRAYTEXT 104 +#define K_DISPLAY_SET_FONT 105 +#define K_DISPLAY_WIDTH 106 +#define K_DISPLAY_SAVE_UNDER 107 +#define K_DISPLAY_RESTORE_UNDER 108 +#define K_DONT_UPDATE_IMMEDIATELY 121 + + +reg_t +kDisplay(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int argpt; + reg_t textp = argv[0]; + int index = UKPV_OR_ALT(1, 0); + int temp; + int save_under = 0; + gfx_color_t transparent; + char *text; + gfxw_port_t *port = (s->port)? s->port : s->picture_port; + int update_immediately = 1; + + gfx_color_t color0, *color1, bg_color; + gfx_alignment_t halign = ALIGN_LEFT; + rect_t area = gfx_rect(port->draw_pos.x, port->draw_pos.y, + 320 - port->draw_pos.x, 200 - port->draw_pos.y); + int gray = port->gray_text; + int font_nr = port->font_nr; + gfxw_text_t *text_handle; + + transparent.mask = 0; + + color0 = port->color; + bg_color = port->bgcolor; + + + if (textp.segment) { + argpt = 1; + text = (char *) kernel_dereference_bulk_pointer(s, textp, 0); + } else { + argpt = 2; + text = kernel_lookup_text(s, textp, index); + } + + if (!text) { + SCIkdebug(SCIkERROR, "Display with invalid reference "PREG"!\n", PRINT_REG(textp)); + return NULL_REG; + } + + while (argpt < argc) { + + switch(UKPV(argpt++)) { + + case K_DISPLAY_SET_COORDS: + + area.x = UKPV(argpt++); + area.y = UKPV(argpt++); + SCIkdebug(SCIkGRAPHICS, "Display: set_coords(%d, %d)\n", area.x, area.y); + break; + + case K_DISPLAY_SET_ALIGNMENT: + + halign = (gfx_alignment_t)KP_SINT(argv[argpt++]); + SCIkdebug(SCIkGRAPHICS, "Display: set_align(%d)\n", halign); + break; + + case K_DISPLAY_SET_COLOR: + + temp = KP_SINT(argv[argpt++]); + SCIkdebug(SCIkGRAPHICS, "Display: set_color(%d)\n", temp); + if ((s->resmgr->sci_version < SCI_VERSION_01_VGA) && temp >= 0 && temp <= 15) + color0 = (s->ega_colors[temp]); + else + if ((s->resmgr->sci_version >= SCI_VERSION_01_VGA) && temp >= 0 && temp < 256) { + color0.visual = *(get_pic_color(s, temp)); + color0.mask = GFX_MASK_VISUAL; + } else + if (temp == -1) + color0 = transparent; + else + SCIkwarn(SCIkWARNING, "Display: Attempt to set invalid fg color %d\n", temp); + break; + + case K_DISPLAY_SET_BGCOLOR: + + temp = KP_SINT(argv[argpt++]); + SCIkdebug(SCIkGRAPHICS, "Display: set_bg_color(%d)\n", temp); + if ((s->resmgr->sci_version < SCI_VERSION_01_VGA) && temp >= 0 && temp <= 15) + bg_color = s->ega_colors[temp]; + else + if ((s->resmgr->sci_version >= SCI_VERSION_01_VGA) && temp >= 0 && temp <= 256) { + bg_color.visual = *get_pic_color(s, temp); + bg_color.mask = GFX_MASK_VISUAL; + } else + if (temp == -1) + bg_color = transparent; + else + SCIkwarn(SCIkWARNING, "Display: Attempt to set invalid fg color %d\n", temp); + break; + + case K_DISPLAY_SET_GRAYTEXT: + + gray = KP_SINT(argv[argpt++]); + SCIkdebug(SCIkGRAPHICS, "Display: set_graytext(%d)\n", gray); + break; + + case K_DISPLAY_SET_FONT: + + font_nr = KP_UINT(argv[argpt++]); + + SCIkdebug(SCIkGRAPHICS, "Display: set_font(\"font.%03d\")\n", font_nr); + break; + + case K_DISPLAY_WIDTH: + + area.xl = UKPV(argpt++); + if (area.xl == 0) + area.xl = MAX_TEXT_WIDTH_MAGIC_VALUE; + + SCIkdebug(SCIkGRAPHICS, "Display: set_width(%d)\n", area.xl); + break; + + case K_DISPLAY_SAVE_UNDER: + + save_under = 1; + SCIkdebug(SCIkGRAPHICS, "Display: set_save_under()\n"); + break; + + case K_DISPLAY_RESTORE_UNDER: + + SCIkdebug(SCIkGRAPHICS, "Display: restore_under(%04x)\n", UKPV(argpt)); + graph_restore_box(s, argv[argpt++]); + update_immediately = 1; + argpt++; + return s->r_acc; + + case K_DONT_UPDATE_IMMEDIATELY: + + update_immediately=0; + SCIkdebug(SCIkGRAPHICS, "Display: set_dont_update()\n"); + argpt++; + break; + + default: + SCIkdebug(SCIkGRAPHICS, "Unknown Display() command %x\n", UKPV(argpt-1)); + return NULL_REG; + } + } + + if (s->version >= SCI_VERSION_FTU_DISPLAY_COORDS_FUZZY) { + if (halign == ALIGN_LEFT) + GFX_ASSERT(gfxop_get_text_params(s->gfx_state, font_nr, text, + area.xl, &area.xl, &area.yl, 0, + NULL, NULL, NULL)); + + /* Make the text fit on the screen */ + if (area.x + area.xl > 320) + area.x += 320 - area.x - area.xl; /* Plus negative number = subtraction */ + + if (area.y + area.yl > 200) + { + area.y += 200 - area.y - area.yl; /* Plus negative number = subtraction */ + } + } + + if (gray) + color1 = &bg_color; + else + color1 = &color0; + + assert_primary_widget_lists(s); + + text_handle = gfxw_new_text(s->gfx_state, area, font_nr, text, halign, + ALIGN_TOP, color0, *color1, bg_color, 0); + + if (!text_handle) { + SCIkwarn(SCIkERROR, "Display: Failed to create text widget!\n"); + return NULL_REG; + } + + if (save_under) { /* Backup */ + rect_t save_area = text_handle->bounds; + save_area.x += port->bounds.x; + save_area.y += port->bounds.y; + + s->r_acc = graph_save_box(s, save_area); + text_handle->serial++; /* This is evil! */ + + SCIkdebug(SCIkGRAPHICS, "Saving (%d, %d) size (%d, %d) as "PREG"\n", + save_area.x, save_area.y, save_area.xl, save_area.yl, s->r_acc); + + } + + + SCIkdebug(SCIkGRAPHICS, "Display: Commiting text '%s'\n", text); + + /* + ADD_TO_CURRENT_FG_WIDGETS(text_handle); + */ + + ADD_TO_CURRENT_FG_WIDGETS(GFXW(text_handle)); + if ((!s->pic_not_valid)&&update_immediately) { /* Refresh if drawn to valid picture */ + FULL_REDRAW(); + SCIkdebug(SCIkGRAPHICS, "Refreshing display...\n"); + } + + return s->r_acc; +} diff --git a/engines/sci/engine/klists.c b/engines/sci/engine/klists.c new file mode 100644 index 0000000000..7637ddd403 --- /dev/null +++ b/engines/sci/engine/klists.c @@ -0,0 +1,608 @@ +/*************************************************************************** + klists.c Copyright (C) 1999 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + +#include + +#ifdef LOOKUP_NODE +# undef LOOKUP_NODE +# define LOOKUP_NODE(addr) inline_lookup_node(s, (addr), __FILE__, __LINE__) +#endif + +inline node_t * +inline_lookup_node(state_t *s, reg_t addr, const char *file, int line) +{ + mem_obj_t *mobj; + node_table_t *nt; + + if (!addr.offset && !addr.segment) + return NULL; /* Non-error null */ + + mobj = GET_SEGMENT(s->seg_manager, addr.segment, MEM_OBJ_NODES); + if (!mobj) { + sciprintf("%s, L%d: Attempt to use non-node "PREG" as list node\n", + __FILE__, __LINE__, PRINT_REG(addr)); + script_debug_flag = script_error_flag = 1; + return NULL; + } + + nt = &(mobj->data.nodes); + + if (!ENTRY_IS_VALID(nt, addr.offset)) { + sciprintf("%s, L%d: Attempt to use non-node "PREG" as list node\n", + __FILE__, __LINE__, PRINT_REG(addr)); + script_debug_flag = script_error_flag = 1; + return NULL; + } + + return &(nt->table[addr.offset].entry); +} + +node_t * +lookup_node(state_t *s, reg_t addr, const char *file, int line) +{ + return inline_lookup_node(s, addr, file, line); +} + +#define LOOKUP_NULL_LIST(addr) _lookup_list(s, addr, __FILE__, __LINE__, 1) + + +inline list_t * +_lookup_list(state_t *s, reg_t addr, const char *file, int line, int may_be_null) +{ + mem_obj_t *mobj; + list_table_t *lt; + + if (may_be_null && !addr.segment && !addr.offset) + return NULL; + + mobj = GET_SEGMENT(s->seg_manager, addr.segment, MEM_OBJ_LISTS); + + if (!mobj) { + sciprintf("%s, L%d: Attempt to use non-list "PREG" as list\n", + __FILE__, __LINE__, PRINT_REG(addr)); + script_debug_flag = script_error_flag = 1; + return NULL; + } + + lt = &(mobj->data.lists); + + if (!ENTRY_IS_VALID(lt, addr.offset)) { + sciprintf("%s, L%d: Attempt to use non-list "PREG" as list\n", + __FILE__, __LINE__, PRINT_REG(addr)); + script_debug_flag = script_error_flag = 1; + return NULL; + } + + return &(lt->table[addr.offset].entry); +} + +list_t * +lookup_list(state_t *s, reg_t addr, const char *file, int line) +{ + return _lookup_list(s, addr, file, line, 0); +} + +int +listp(state_t *s, reg_t addr) +{ + return (s->seg_manager.heap[addr.segment]->type == MEM_OBJ_LISTS + && ENTRY_IS_VALID(&(s->seg_manager.heap[addr.segment]->data.lists), addr.offset)); +} + +#ifdef DISABLE_VALIDATIONS + +#define sane_nodep(a, b) 1 +#define sane_listp(a, b) 1 + +#else + +static inline int +sane_nodep(state_t *s, reg_t addr) +{ + int have_prev = 0; + reg_t prev; + + do { + node_t *node = LOOKUP_NODE(addr); + + if (!node) + return 0; + + if ((have_prev) + && !REG_EQ(node->pred, prev)) + return 0; + + prev = addr; + addr = node->succ; + have_prev = 1; + + } while (!IS_NULL_REG(addr)); + + return 1; +} + + +int +sane_listp(state_t *s, reg_t addr) +{ + list_t *l = LOOKUP_LIST(addr); + int empties = 0; + + if (IS_NULL_REG(l->first)) + ++empties; + if (IS_NULL_REG(l->last)) + ++empties; + + /* Either none or both must be set */ + if (empties == 1) + return 0; + + if (!empties) { + node_t *node_a, *node_z; + + node_a = LOOKUP_NODE(l->first); + node_z = LOOKUP_NODE(l->last); + + if (!node_a || !node_z) + return 0; + + if (!IS_NULL_REG(node_a->pred)) + return 0; + + if (!IS_NULL_REG(node_z->succ)) + return 0; + + return sane_nodep(s, l->first); + } + + return 1; /* Empty list is fine */ +} +#endif + + +reg_t +kNewList(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t listbase; + list_t *l; + l = sm_alloc_list(&s->seg_manager, &listbase); + l->first = l->last = NULL_REG; + SCIkdebug(SCIkNODES, "New listbase at "PREG"\n", PRINT_REG(listbase)); + + return listbase; /* Return list base address */ +} + +reg_t +kDisposeList(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + list_t *l = LOOKUP_LIST(argv[0]); + + if (!l) { + SCIkwarn(SCIkERROR, "Attempt to dispose non-list at "PREG"!\n", + PRINT_REG(argv[0])); + return NULL_REG; + } + + if (!sane_listp(s, argv[0])) + SCIkwarn(SCIkERROR,"List at "PREG" is not sane anymore!\n", PRINT_REG(argv[0])); + +/* if (!IS_NULL_REG(l->first)) { + reg_t n_addr = l->first; + + while (!IS_NULL_REG(n_addr)) { /-* Free all nodes *-/ + node_t *n = LOOKUP_NODE(n_addr); + sm_free_node(&s->seg_manager, n_addr); + n_addr = n->succ; + } + } + + sm_free_list(&s->seg_manager, argv[0]); +*/ + + return s->r_acc; +} + + +inline reg_t +_k_new_node(state_t *s, reg_t value, reg_t key) +{ + reg_t nodebase; + node_t *n = sm_alloc_node(&s->seg_manager, &nodebase); + + if (!n) { + KERNEL_OOPS("Out of memory while creating a node"); + return NULL_REG; + } + + n->pred = n->succ = NULL_REG; + n->key = key; + n->value = value; + + return nodebase; +} + +reg_t +kNewNode(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + s->r_acc = _k_new_node(s, argv[0], argv[1]); + + SCIkdebug(SCIkNODES, "New nodebase at "PREG"\n", PRINT_REG(s->r_acc)); + + return s->r_acc; +} + + + +reg_t +kFirstNode(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + list_t *l = LOOKUP_NULL_LIST(argv[0]); + + if (l && !sane_listp(s, argv[0])) + SCIkwarn(SCIkERROR,"List at "PREG" is not sane anymore!\n", + PRINT_REG(argv[0])); + + if (l) + return l->first; + else + return NULL_REG; +} + + +reg_t +kLastNode(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + list_t *l = LOOKUP_LIST(argv[0]); + + if (l&&!sane_listp(s, argv[0])) + SCIkwarn(SCIkERROR,"List at "PREG" is not sane anymore!\n", + PRINT_REG(argv[0])); + + if (l) + return l->last; + else + return NULL_REG; +} + + +reg_t +kEmptyList(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + list_t *l = LOOKUP_LIST(argv[0]); + + if (!l || !sane_listp(s, argv[0])) + SCIkwarn(SCIkERROR,"List at "PREG" is invalid or not sane anymore!\n", + PRINT_REG(argv[0])); + + return make_reg(0, ((l)? IS_NULL_REG(l->first) : 0)); +} + +inline void +_k_add_to_front(state_t *s, reg_t listbase, reg_t nodebase) +{ + list_t *l = LOOKUP_LIST(listbase); + node_t *new_n = LOOKUP_NODE(nodebase); + + SCIkdebug(SCIkNODES, "Adding node "PREG" to end of list "PREG"\n", + PRINT_REG(nodebase), PRINT_REG(listbase)); + + if (!new_n) + SCIkwarn(SCIkERROR, "Attempt to add non-node ("PREG") to list at "PREG"\n", + PRINT_REG(nodebase), PRINT_REG(listbase)); + if (!l || !sane_listp(s, listbase)) + SCIkwarn(SCIkERROR,"List at "PREG" is not sane anymore!\n", PRINT_REG(listbase)); + + new_n->succ = l->first; + new_n->pred = NULL_REG; + /* Set node to be the first and last node if it's the only node of the list */ + if (IS_NULL_REG(l->first)) + l->last = nodebase; + else { + node_t *old_n = LOOKUP_NODE(l->first); + old_n->pred = nodebase; + } + l->first = nodebase; +} + +inline void +_k_add_to_end(state_t *s, reg_t listbase, reg_t nodebase) +{ + list_t *l = LOOKUP_LIST(listbase); + node_t *new_n = LOOKUP_NODE(nodebase); + + SCIkdebug(SCIkNODES, "Adding node "PREG" to end of list "PREG"\n", + PRINT_REG(nodebase), PRINT_REG(listbase)); + + if (!new_n) + SCIkwarn(SCIkERROR, "Attempt to add non-node ("PREG") to list at "PREG"\n", + PRINT_REG(nodebase), PRINT_REG(listbase)); + if (!l || !sane_listp(s, listbase)) + SCIkwarn(SCIkERROR,"List at "PREG" is not sane anymore!\n", PRINT_REG(listbase)); + + new_n->succ = NULL_REG; + new_n->pred = l->last; + /* Set node to be the first and last node if it's the only node of the list */ + if (IS_NULL_REG(l->last)) + l->first = nodebase; + else { + node_t *old_n = LOOKUP_NODE(l->last); + old_n->succ = nodebase; + } + l->last = nodebase; +} + + +reg_t +kNextNode(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + node_t *n = LOOKUP_NODE(argv[0]); + if (!sane_nodep(s, argv[0])) + { + SCIkwarn(SCIkERROR,"List node at "PREG" is not sane anymore!\n", + PRINT_REG(argv[0])); + script_error_flag = script_debug_flag = 0; + return NULL_REG; + } + + return n->succ; +} + +reg_t +kPrevNode(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + node_t *n = LOOKUP_NODE(argv[0]); + if (!sane_nodep(s, argv[0])) + SCIkwarn(SCIkERROR,"List node at "PREG" is not sane anymore!\n", + PRINT_REG(argv[0])); + + return n->pred; +} + + +reg_t +kNodeValue(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + node_t *n = LOOKUP_NODE(argv[0]); + if (!sane_nodep(s, argv[0])) + { + SCIkwarn(SCIkERROR,"List node at "PREG" is not sane!\n", + PRINT_REG(argv[0])); + script_debug_flag = script_error_flag = 0; + return NULL_REG; + } + + return n->value; +} + + +reg_t +kAddToFront(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + _k_add_to_front(s, argv[0], argv[1]); + return s->r_acc; +} + +reg_t +kAddAfter(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + list_t *l = LOOKUP_LIST(argv[0]); + node_t *firstnode = IS_NULL_REG(argv[1])? NULL : LOOKUP_NODE(argv[1]); + node_t *newnode = LOOKUP_NODE(argv[2]); + + if (!l || !sane_listp(s, argv[0])) + SCIkwarn(SCIkERROR,"List at "PREG" is not sane anymore!\n", + PRINT_REG(argv[0])); + + if (!newnode) { + SCIkwarn(SCIkERROR,"New 'node' "PREG" is not a node!\n", + argv[1], argv[2]); + return NULL_REG; + } + + if (argc != 3) { + SCIkdebug(SCIkWARNING, "Aborting.\n"); + return NULL_REG; + } + + if (firstnode) { /* We're really appending after */ + reg_t oldnext = firstnode->succ; + + newnode->pred = argv[1]; + firstnode->succ = argv[2]; + newnode->succ = oldnext; + + if (IS_NULL_REG(oldnext)) /* Appended after last node? */ + /* Set new node as last list node */ + l->last = argv[2]; + else + LOOKUP_NODE(oldnext)->pred = argv[2]; + + return s->r_acc; + + } else { /* !firstnode */ + /* Prepare call to AddToFront... */ + argv[1] = argv[0]; + return kAddToFront(s, funct_nr, 2, argv + 1);/* Set as initial list node */ + } +} + + +reg_t +kAddToEnd(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + _k_add_to_end(s, argv[0], argv[1]); + return s->r_acc; +} + + +reg_t +kFindKey(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t node_pos; + reg_t key = argv[1]; + reg_t list_pos = argv[0]; + + SCIkdebug(SCIkNODES, "Looking for key "PREG" in list "PREG"\n", + PRINT_REG(key), PRINT_REG(list_pos)); + + if (!sane_listp(s, list_pos)) + SCIkwarn(SCIkERROR,"List at "PREG" is not sane anymore!\n", + PRINT_REG(list_pos)); + + node_pos = LOOKUP_LIST(list_pos)->first; + + SCIkdebug(SCIkNODES, "First node at "PREG"\n", PRINT_REG(node_pos)); + + while (!IS_NULL_REG(node_pos)) { + node_t *n = LOOKUP_NODE(node_pos); + if (REG_EQ(n->key, key)) { + SCIkdebug(SCIkNODES, " Found key at "PREG"\n", PRINT_REG(node_pos)); + return node_pos; + } + + node_pos = n->succ; + SCIkdebug(SCIkNODES, "NextNode at "PREG"\n", PRINT_REG(node_pos)); + } + + SCIkdebug(SCIkNODES, "Looking for key without success\n"); + return NULL_REG; +} + + +reg_t +kDeleteKey(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t node_pos = kFindKey(s, funct_nr, 2, argv); + node_t *n; + list_t *l = LOOKUP_LIST(argv[0]); + + if (IS_NULL_REG(node_pos)) + return NULL_REG; /* Signal falure */ + + n = LOOKUP_NODE(node_pos); + if (REG_EQ(l->first, node_pos)) + l->first = n->succ; + if (REG_EQ(l->last, node_pos)) + l->last = n->pred; + + if (!IS_NULL_REG(n->pred)) + LOOKUP_NODE(n->pred)->succ = n->succ; + if (!IS_NULL_REG(n->succ)) + LOOKUP_NODE(n->succ)->pred = n->pred; + +/* sm_free_node(&s->seg_manager, node_pos);*/ + + return make_reg(0, 1); /* Signal success */ +} + + + + +typedef struct +{ + reg_t key, value; + reg_t order; +} sort_temp_t; + +int +sort_temp_cmp(const void *p1, const void *p2) +{ + sort_temp_t *st1 = (sort_temp_t *) p1; + sort_temp_t *st2 = (sort_temp_t *) p2; + + if (st1->order.segment < st1->order.segment || + (st1->order.segment == st1->order.segment && + st1->order.offset < st2->order.offset)) + return -1; + + if (st1->order.segment > st2->order.segment || + (st1->order.segment == st2->order.segment && + st1->order.offset > st2->order.offset)) + return 1; + + return 0; +} + +reg_t +kSort(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t source = argv[0]; + reg_t dest = argv[1]; + reg_t order_func = argv[2]; + + int input_size = GET_SEL32SV(source, size); + + int i; + + sort_temp_t *temp_array = (sort_temp_t *) + malloc(sizeof(sort_temp_t)*input_size); + + reg_t input_data = GET_SEL32(source, elements); + reg_t output_data = GET_SEL32(dest, elements); + + list_t *list; + node_t *node; + + if (!input_size) + return s->r_acc; + + if (IS_NULL_REG(output_data)) + { + list = sm_alloc_list(&s->seg_manager, &output_data); + list->first = list->last = NULL_REG; + PUT_SEL32(dest, elements, output_data); + } + + PUT_SEL32V(dest, size, input_size); + + list = LOOKUP_LIST(input_data); + node = LOOKUP_NODE(list->first); + + i = 0; + while (node) + { + invoke_selector(INV_SEL(order_func, doit, 0), 1, node->value); + temp_array[i].key = node->key; + temp_array[i].value = node->value; + temp_array[i].order = s->r_acc; + i++; + node = LOOKUP_NODE(node->succ); + } + + qsort(temp_array, input_size, sizeof(sort_temp_t), sort_temp_cmp); + + for (i=0;ir_acc; +} diff --git a/engines/sci/engine/kmath.c b/engines/sci/engine/kmath.c new file mode 100644 index 0000000000..94480ab3bd --- /dev/null +++ b/engines/sci/engine/kmath.c @@ -0,0 +1,201 @@ +/*************************************************************************** + kmath.c Copyright (C) 1999 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + +#include + + +reg_t +kRandom(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + return make_reg(0, + SKPV(0) + (int) ((SKPV(1) + 1.0 - SKPV(0)) * (rand() / (RAND_MAX + 1.0)))); +} + + +reg_t +kAbs(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + /* This is a hack, but so is the code in Hoyle1 that needs it. */ + if (argv[0].segment) + return make_reg(0, 0x3e8); /* Yes people, this is an object */ + return make_reg(0, abs(SKPV(0))); +} + + +reg_t +kSqrt(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + return make_reg(0, (gint16) sqrt((float) abs(SKPV(0)))); +} + + +int +get_angle(int xrel, int yrel) +{ + if ((xrel == 0) && (yrel == 0)) + return 0; + else { + int val = (int) (180.0/PI * atan2(xrel, -yrel)); + if (val < 0) + val += 360; + + /* Take care of OB1 differences between SSCI and + FSCI. SCI games sometimes check for equality with + "round" angles */ + if (val % 45 == 44) + val++; + else if (val % 45 == 1) + val--; + + return val; + } +} + +reg_t +kGetAngle(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + /* Based on behavior observed with a test program created with + ** SCI Studio. + */ + int x1 = SKPV(0); + int y1 = SKPV(1); + int x2 = SKPV(2); + int y2 = SKPV(3); + int xrel = x2 - x1; + int yrel = y1 - y2; /* y-axis is mirrored. */ + int angle; + + /* Move (xrel, yrel) to first quadrant. */ + if (y1 < y2) + yrel = -yrel; + if (x2 < x1) + xrel = -xrel; + + /* Compute angle in grads. */ + if (yrel == 0 && xrel == 0) + angle = 0; + else + angle = 100 * xrel / (xrel + yrel); + + /* Fix up angle for actual quadrant of (xrel, yrel). */ + if (y1 < y2) + angle = 200 - angle; + if (x2 < x1) + angle = 400 - angle; + + /* Convert from grads to degrees by merging grad 0 with grad 1, + ** grad 10 with grad 11, grad 20 with grad 21, etc. This leads to + ** "degrees" that equal either one or two grads. + */ + angle -= (angle + 9) / 10; + + return make_reg(0, angle); +} + + +reg_t +kGetDistance(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int xrel = (int) (((float) SKPV(1) - SKPV_OR_ALT(3, 0))/cos(SKPV_OR_ALT(5, 0)* PI / 180.0)); /* This works because cos(0)==1 */ + int yrel = SKPV(0) - SKPV_OR_ALT(2, 0); + + return make_reg(0, (gint16)sqrt((float) xrel*xrel + yrel*yrel)); +} + +reg_t +kTimesSin(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int angle = SKPV(0); + int factor = SKPV(1); + + return make_reg(0, (int) (factor * 1.0 * sin(angle * PI / 180.0))); +} + + +reg_t +kTimesCos(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int angle = SKPV(0); + int factor = SKPV(1); + + return make_reg(0, (int) (factor * 1.0 * cos(angle * PI / 180.0))); +} + +reg_t +kCosDiv(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int angle = SKPV(0); + int value = SKPV(1); + double cosval = cos(angle * PI / 180.0); + + if ((cosval < 0.0001) && (cosval > 0.0001)) { + SCIkwarn(SCIkWARNING,"Attepted division by zero\n"); + return make_reg(0, (gint16)0x8000); + } else + return make_reg(0, (gint16) (value/cosval)); +} + +reg_t +kSinDiv(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int angle = SKPV(0); + int value = SKPV(1); + double sinval = sin(angle * PI / 180.0); + + if ((sinval < 0.0001) && (sinval > 0.0001)) { + SCIkwarn(SCIkWARNING,"Attepted division by zero\n"); + return make_reg(0, (gint16)0x8000); + } else + return make_reg(0, (gint16) (value/sinval)); +} + +reg_t +kTimesTan(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int param = SKPV(0); + int scale = SKPV_OR_ALT(1, 1); + + param -= 90; + if ((param % 90) == 0) { + SCIkwarn(SCIkWARNING, "Attempted tan(pi/2)"); + return make_reg(0, (gint16)0x8000); + } else + return make_reg(0, (gint16) -(tan(param * PI / 180.0) * scale)); +} + +reg_t +kTimesCot(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int param = SKPV(0); + int scale = SKPV_OR_ALT(1, 1); + + if ((param % 90) == 0) { + SCIkwarn(SCIkWARNING, "Attempted tan(pi/2)"); + return make_reg(0, (gint16)0x8000); + } else + return make_reg(0, (gint16) (tan(param * PI / 180.0) * scale)); +} diff --git a/engines/sci/engine/kmenu.c b/engines/sci/engine/kmenu.c new file mode 100644 index 0000000000..4530d5c34f --- /dev/null +++ b/engines/sci/engine/kmenu.c @@ -0,0 +1,533 @@ +/*************************************************************************** + kmenu.c Copyright (C) 1999 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + +#include +#include +#include + + +reg_t +kAddMenu(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char *name = kernel_dereference_char_pointer(s, argv[0], 0); + char *contents = kernel_dereference_char_pointer(s, argv[1], 0); + + menubar_add_menu(s->gfx_state, s->menubar, name, + contents, s->titlebar_port->font_nr, argv[1]); + + return s->r_acc; + +} + + +reg_t +kSetMenu(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int index = UKPV(0); + int i = 2; + + while (i < argc) { + menubar_set_attribute(s, (index >> 8) - 1, (index & 0xff) - 1, UKPV(i - 1), argv[i]); + i += 2; + } + + return s->r_acc; +} + +reg_t +kGetMenu(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int index = UKPV(0); + + return menubar_get_attribute(s, (index >> 8) - 1, (index & 0xff) - 1, UKPV(1)); +} + + +reg_t +kDrawStatus(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t text = argv[0]; + int fgcolor = SKPV_OR_ALT(1, s->status_bar_foreground); + int bgcolor = SKPV_OR_ALT(2, s->status_bar_background); + + s->titlebar_port->color.visual = *(get_pic_color(s, fgcolor)); + s->titlebar_port->color.mask = GFX_MASK_VISUAL; + s->titlebar_port->bgcolor.visual = *(get_pic_color(s, bgcolor)); + s->titlebar_port->bgcolor.mask = GFX_MASK_VISUAL; + + s->status_bar_foreground=fgcolor; + s->status_bar_background=bgcolor; + + if (NULL != s->status_bar_text) { + sci_free(s->status_bar_text); + s->status_bar_text = NULL; + } + + if (text.segment) + s->status_bar_text = sci_strdup(kernel_dereference_char_pointer(s, text, 0)); + + sciw_set_status_bar(s, s->titlebar_port, s->status_bar_text, fgcolor, bgcolor); + + gfxop_update(s->gfx_state); + + return s->r_acc; +} + + +reg_t +kDrawMenuBar(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + + if (SKPV(0)) + sciw_set_menubar(s, s->titlebar_port, s->menubar, -1); + else + sciw_set_status_bar(s, s->titlebar_port, NULL, 0, 0); + + s->titlebar_port->draw(GFXW(s->titlebar_port), gfx_point(0, 0)); + gfxop_update(s->gfx_state); + + return s->r_acc; +} + + +#define ABOUT_FREESCI_PAGES_NR 7 + +static struct { + const char *title; + const char *body; + int fgcolor, bgcolor; +} _about_freesci_pages[ABOUT_FREESCI_PAGES_NR] = { + {"FreeSCI hackers and contributors", + "Alexander R. Angas\nDirectX 8 driver, Event sound server, Generic memory functions" + "\n\n" + "Anders Baden Nielsen\nPPC testing" + "\n\n" + "Bas Zoetekouw\nMan pages, debian package management, CVS maintenance" + "\n\n" + "Carl Muckenhoupt\nSources to the SCI resource viewer tools that started it all" + "\n\n" + "Chris Kehler\nMakefile enhancements" + "\n\n" + "Christoph Reichenbach\nUN*X code, VM/Graphics/Sound/other infrastructure" + "\n\n" + "Christopher T. Lansdown\nOriginal CVS maintainer, Alpha compatibility fixes" + ,0, 15}, + {"More FreeSCI hackers and contributors", + "Claudio Matsuoka\nCVS snapshots, daily builds, BeOS and cygwin ports" + "\n\n" + "Dark Minister\nSCI research (bytecode and parser)" + "\n\n" + "Dmitry Jemerov\nPort to the Win32 platform, numerous bugfixes" + "\n\n" + "Emmanuel Jeandel\nBugfixes and bug reports" + "\n\n" + "Francois-R Boyer\nMT-32 information and mapping code" + "\n\n" + "George Reid\nFreeBSD package management" + "\n\n" + "Hubert Maier\nAmigaOS 4 port" + ,0, 15}, + {"Even more FreeSCI hackers & contributors", + "Hugues Valois\nGame selection menu" + "\n\n" + "Johannes Manhave\nDocument format translation" + "\n\n" + "Jordi Vilalta\nNumerous code and website clean-up patches" + "\n\n" + "Lars Skovlund\nProject maintenance, most documentation, bugfixes, SCI1 support" + "\n\n" + "Magnus Reftel\nHeap implementation, Python class viewer, bugfixes" + "\n\n" + "Matt Hargett\nClean-ups, bugfixes, Hardcore QA, Win32" + "\n\n" + "Max Horn\nSetJump implementation" + ,0, 15}, + {"Still more of them", + "Paul David Doherty\nGame version information" + "\n\n" + "Petr Vyhnak\nThe DCL-INFLATE algorithm, many Win32 improvements" + "\n\n" + "Rainer Canavan\nIRIX MIDI driver and bug fixes" + "\n\n" + "Rainer De Temple\nSCI research" + "\n\n" + "Ravi I.\nSCI0 sound resource specification" + "\n\n" + "Ruediger Hanke\nPort to the MorphOS platform" + "\n\n" + "Rune Orsval\nConfiguration file editor" + ,0, 15}, + {"Is there no end to these contributors?", + "Rickard Lind\nMT32->GM MIDI mapping magic, sound research" + "\n\n" + "Rink Springer\nPort to the DOS platform, several bug fixes" + "\n\n" + "Robey Pointer\nBug tracking system hosting" + "\n\n" + "Sergey Lapin\nPort of Carl's type 2 decompression code" + "\n\n" + "Solomon Peachy\nSDL ports and much of the sound subsystem" + "\n\n" + "Vyacheslav Dikonov\nConfig script improvements" + "\n\n" + "Walter van Niftrik\nPorts to the Dreamcast and GP32 platforms", + 0, 15}, + {"The CSCI5573 Team at CU Boulder", + "Xiaojun Chen\nSean Terrell\nChristoph Reichenbach\n\n" + "Special thanks to Prof. Dr. Gary Nutt\n\nfor allowing the FreeSCI VM extension as a\ncourse project in his Advanced OS course" + ,0, 15}, + {"Special Thanks", + "Special Thanks as well\n\n\nto the linuxgames.com and telefragged.com crew\nfor hosting us\n\n" + "To the savannah.gnu.org staff\nfor hosting our mailing list\n\n" + "To Bob Heitman and Corey Cole for their support" + ,0, 15} +}; + + +void +about_freesci(state_t *s) +{ + int page; + gfxw_port_t *port; + int bodyfont, titlefont; + resource_t *bodyfont_res = NULL; + int i; + + titlefont = s->titlebar_port->font_nr; + + i = 999; + while (!bodyfont_res && (i > -1)) + bodyfont_res = scir_test_resource(s->resmgr, sci_font, i--); + + if (i == -1) { + sciprintf("Sorry, couldn't find a font...\n"); + return; + } + + bodyfont = i+1; + for (page = 0; page < ABOUT_FREESCI_PAGES_NR; ++page) { + sci_event_t event; + int cont = 2; + int width, height, width2, foo; + + _about_freesci_pages[page].fgcolor = 0; + _about_freesci_pages[page].bgcolor = 15; + + gfxop_get_text_params(s->gfx_state, bodyfont, _about_freesci_pages[page].body, 300, &width, &height, 0, + NULL, NULL, NULL); + gfxop_get_text_params(s->gfx_state, titlefont, _about_freesci_pages[page].title, 300, &width2, &foo, 0, + NULL, NULL, NULL); + + width += 4; + width2 += 4; + height += 12; + + if (width2 > width) + width = width2; + + port = sciw_new_window(s, gfx_rect(156 - (width >> 1), 100 - (height >> 1), width, height), + bodyfont, s->ega_colors[_about_freesci_pages[page].fgcolor], + s->ega_colors[_about_freesci_pages[page].bgcolor], + titlefont, s->ega_colors[15], s->ega_colors[0], + _about_freesci_pages[page].title, WINDOW_FLAG_TITLE); + + port->add(GFXWC(port), GFXW(gfxw_new_text(s->gfx_state, gfx_rect(0,0,width,height), bodyfont, + _about_freesci_pages[page].body, + ALIGN_CENTER, ALIGN_CENTER, port->color, port->color, + port->bgcolor, 0) + )); + + s->visual->add(GFXWC(s->visual), GFXW(port)); + + port->add_dirty_abs(GFXWC(port), gfx_rect_fullscreen, 1); + s->visual->draw(GFXW(s->visual), gfx_point(0,0)); + gfxop_update(s->gfx_state); + + while (cont) { + event = gfxop_get_event(s->gfx_state, SCI_EVT_ANY); + + if (event.type == SCI_EVT_MOUSE_RELEASE || event.type == SCI_EVT_MOUSE_PRESS) + --cont; + + if (event.type == SCI_EVT_QUIT) { + quit_vm(); + return; + } + + if (event.type == SCI_EVT_KEYBOARD) + cont = 0; + + gfxop_usleep(s->gfx_state, 25000); + } + + + port->widfree(GFXW(port)); + s->visual->draw(GFXW(s->visual), gfx_point(0,0)); + gfxop_update(s->gfx_state); + + } +} + + +static inline int +_menu_go_down(state_t *s, int menu_nr, int item_nr) +{ + int seeker, max = s->menubar->menus[menu_nr].items_nr; + seeker = item_nr + 1; + + while ((seeker < max) && !menubar_item_valid(s, menu_nr, seeker)) + ++seeker; + + if (seeker != max) + return seeker; + else return item_nr; +} + +#define FULL_REDRAW \ + s->visual->draw(GFXW(s->visual), gfx_point(0, 0)); \ + gfxop_update(s->gfx_state); + + +reg_t +kMenuSelect(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t event = argv[0]; + /*int pause_sound = UKPV_OR_ALT(1, 1);*/ /* FIXME: Do this eventually */ + int claimed = 0; + int type = GET_SEL32V(event, type); + int message = GET_SEL32V(event, message); + int modifiers = GET_SEL32V(event, modifiers); + int menu_nr = -1, item_nr; + menu_item_t *item; + int menu_mode = 0; /* Menu is active */ + int mouse_down = 0; + + gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen); + + /* Check whether we can claim the event directly as a keyboard or said event */ + if (type & (SCI_EVT_KEYBOARD | SCI_EVT_SAID)) { + int menuc, itemc; + + if ((type == SCI_EVT_KEYBOARD) + && (message == SCI_K_ESC)) + menu_mode = 1; + + else if ((type == SCI_EVT_SAID) || message) { /* Don't claim 0 keyboard event */ + SCIkdebug(SCIkMENU,"Menu: Got %s event: %04x/%04x\n", + ((type == SCI_EVT_SAID)? "SAID":"KBD"), message, modifiers); + + for (menuc = 0; menuc < s->menubar->menus_nr; menuc++) + for (itemc = 0; itemc < s->menubar->menus[menuc].items_nr; itemc++) { + item = s->menubar->menus[menuc].items + itemc; + + SCIkdebug(SCIkMENU,"Menu: Checking against %s: %04x/%04x (type %d, %s)\n", + item->text? item->text : "--bar--", item->key, item->modifiers, + item->type, item->enabled? "enabled":"disabled"); + + if (((item->type == MENU_TYPE_NORMAL) + && (item->enabled)) + && (((type == SCI_EVT_KEYBOARD) /* keyboard event */ + && menubar_match_key(item, message, modifiers)) + || ((type == SCI_EVT_SAID) /* Said event */ + && (item->flags & MENU_ATTRIBUTE_FLAGS_SAID) + && (said(s, item->said, (s->debug_mode & (1 << SCIkPARSER_NR))) != SAID_NO_MATCH) + ) + ) + ) { + /* Claim the event */ + SCIkdebug(SCIkMENU,"Menu: Event CLAIMED for %d/%d\n", menuc, itemc); + claimed = 1; + menu_nr = menuc; + item_nr = itemc; + } + } + } + } + + if ((type == SCI_EVT_MOUSE_PRESS) && (s->gfx_state->pointer_pos.y < 10)) { + menu_mode = 1; + mouse_down = 1; + } + + if (menu_mode) { + int old_item; + int old_menu; + gfxw_port_t *port = NULL; + + item_nr = -1; + + /* Default to menu 0, unless the mouse was used to generate this effect */ + if (mouse_down) + menubar_map_pointer(s, &menu_nr, &item_nr, port); + else + menu_nr = 0; + + sciw_set_menubar(s, s->titlebar_port, s->menubar, menu_nr); + FULL_REDRAW; + + old_item = -1; + old_menu = -1; + + while (menu_mode) { + sci_event_t event = gfxop_get_event(s->gfx_state, SCI_EVT_ANY); + + claimed = 0; + + switch (event.type) { + case SCI_EVT_QUIT: + quit_vm(); + return NULL_REG; + + case SCI_EVT_KEYBOARD: + switch (event.data) { + + case '`': + if (event.buckybits & SCI_EVM_CTRL) + s->visual->print(GFXW(s->visual), 0); + break; + + case SCI_K_ESC: + menu_mode = 0; + break; + + case SCI_K_ENTER: + menu_mode = 0; + if ((item_nr >= 0) && (menu_nr >= 0)) + claimed = 1; + break; + + case SCI_K_LEFT: + if (menu_nr > 0) + --menu_nr; + else + menu_nr = s->menubar->menus_nr - 1; + + item_nr = _menu_go_down(s, menu_nr, -1); + break; + + case SCI_K_RIGHT: + if (menu_nr < (s->menubar->menus_nr - 1)) + ++menu_nr; + else + menu_nr = 0; + + item_nr = _menu_go_down(s, menu_nr, -1); + break; + + case SCI_K_UP: + if (item_nr > -1) { + + do { --item_nr; } + while ((item_nr > -1) && !menubar_item_valid(s, menu_nr, item_nr)); + } + break; + + case SCI_K_DOWN: { + item_nr = _menu_go_down(s, menu_nr, item_nr); + } + break; + + } + break; + + case SCI_EVT_MOUSE_RELEASE: + menu_mode = (s->gfx_state->pointer_pos.y < 10); + claimed = !menu_mode && !menubar_map_pointer(s, &menu_nr, &item_nr, port); + mouse_down = 0; + break; + + case SCI_EVT_MOUSE_PRESS: + mouse_down = 1; + break; + + case SCI_EVT_NONE: + gfxop_usleep(s->gfx_state, 2500); + break; + } + + if (mouse_down) + menubar_map_pointer(s, &menu_nr, &item_nr, port); + + if ((item_nr > -1 && old_item == -1) || (menu_nr != old_menu)) { /* Update menu */ + + sciw_set_menubar(s, s->titlebar_port, s->menubar, menu_nr); + + if (port) + port->widfree(GFXW(port)); + + port = sciw_new_menu(s, s->titlebar_port, s->menubar, menu_nr); + s->wm_port->add(GFXWC(s->wm_port), GFXW(port)); + + if (item_nr > -1) + old_item = -42; /* Enforce redraw in next step */ + else { + FULL_REDRAW; + } + + } /* ...if the menu changed. */ + + /* Remove the active menu item, if neccessary */ + if (item_nr != old_item) { + port = sciw_unselect_item(s, port, s->menubar->menus + menu_nr, old_item); + port = sciw_select_item(s, port, s->menubar->menus + menu_nr, item_nr); + FULL_REDRAW; + } + + old_item = item_nr; + old_menu = menu_nr; + + } /* while (menu_mode) */ + + if (port) { + port->widfree(GFXW(port)); + port = NULL; + + sciw_set_status_bar(s, s->titlebar_port, s->status_bar_text, s->status_bar_foreground, s->status_bar_background); + gfxop_update(s->gfx_state); + } + FULL_REDRAW; + } + + if (claimed) { + PUT_SEL32(event, claimed, make_reg(0, 1)); + + if (menu_nr > -1) { + s->r_acc = make_reg(0, ((menu_nr + 1) << 8) | (item_nr + 1)); +#ifdef MENU_FREESCI_BLATANT_PLUG + if (s->menubar->menus[menu_nr].items[item_nr].flags == MENU_FREESCI_BLATANT_PLUG) + about_freesci(s); +#endif + + } else + s->r_acc = NULL_REG; + + SCIkdebug(SCIkMENU, "Menu: Claim -> %04x\n", s->r_acc.offset); + } + else s->r_acc = NULL_REG; /* Not claimed */ + + return s->r_acc; +} diff --git a/engines/sci/engine/kmovement.c b/engines/sci/engine/kmovement.c new file mode 100644 index 0000000000..5f7d1a7281 --- /dev/null +++ b/engines/sci/engine/kmovement.c @@ -0,0 +1,580 @@ +/*************************************************************************** + kmovement.c Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include + +/* +Compute "velocity" vector (xStep,yStep)=(vx,vy) for a jump from (0,0) to (dx,dy), with gravity gy. +The gravity is assumed to be non-negative. + +If this was ordinary continuous physics, we would compute the desired (floating point!) +velocity vector (vx,vy) as follows, under the assumption that vx and vy are linearly correlated +by some constant factor c, i.e. vy = c * vx: + dx = t * vx + dy = t * vy + gy * t^2 / 2 +=> dy = c * dx + gy * (dx/vx)^2 / 2 +=> |vx| = sqrt( gy * dx^2 / (2 * (dy - c * dx)) ) +Here, the sign of vx must be chosen equal to the sign of dx, obviously. + +Clearly, this square root only makes sense in our context if the denominator is positive, +or equivalently, (dy - c * dx) must be positive. For simplicity and by symmetry +along the x-axis, we assume dx to be positive for all computations, and only adjust for +its sign in the end. Switching the sign of c appropriately, we set tmp := (dy + c * dx) +and compute c so that this term becomes positive. + +Remark #1: If the jump is straight up, i.e. dx == 0, then we should not assume the above +linear correlation vy = c * vx of the velocities (as vx will be 0, but vy shouldn't be, +unless we drop). + + +Remark #2: We are actually in a discrete setup. The motion is computed iteratively: each iteration, +we add vx and vy to the position, then add gy to vy. So the real formula is the following +(where t is ideally close to an int): + + dx = t * vx + dy = t * vy + gy * t*(t-1) / 2 + +But the solution resulting from that is a lot more complicated, so we use the above approximation instead. + +Still, what we compute in the end is of course not a real velocity anymore, but an integer approximation, +used in an iterative stepping algorithm +*/ +reg_t +kSetJump(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + // Input data + reg_t object = argv[0]; + int dx = SKPV(1); + int dy = SKPV(2); + int gy = SKPV(3); + + // Derived data + int c; + int tmp; + int vx = 0; // x velocity + int vy = 0; // y velocity + + int dxWasNegative = (dx < 0); + dx = abs(dx); + + assert(gy >= 0); + + if (dx == 0) { + // Upward jump. Value of c doesn't really matter + c = 1; + } else { + // Compute a suitable value for c respectively tmp. + // The important thing to consider here is that we want the resulting + // *discrete* x/y velocities to be not-too-big integers, for a smooth + // curve (i.e. we could just set vx=dx, vy=dy, and be done, but that + // is hardly what you would call a parabolic jump, would ya? ;-). + // + // So, we make sure that 2.0*tmp will be bigger than dx (that way, + // we ensure vx will be less than sqrt(gy * dx)). + if (dx + dy < 0) { + // dy is negative and |dy| > |dx| + c = (2*abs(dy)) / dx; + //tmp = abs(dy); // ALMOST the resulting value, except for obvious rounding issues + } else { + // dy is either positive, or |dy| <= |dx| + c = (dx*3/2 - dy) / dx; + + // We force c to be strictly positive + if (c < 1) + c = 1; + + //tmp = dx*3/2; // ALMOST the resulting value, except for obvious rounding issues + + // FIXME: Where is the 3 coming from? Maybe they hard/coded, by "accident", that usually gy=3 ? + // Then this choice of will make t equal to roughly sqrt(dx) + } + } + // POST: c >= 1 + tmp = c * dx + dy; + // POST: (dx != 0) ==> abs(tmp) > abs(dx) + // POST: (dx != 0) ==> abs(tmp) ~>=~ abs(dy) + + + SCIkdebug(SCIkBRESEN, "c: %d, tmp: %d\n", c, tmp); + + // Compute x step + if (tmp != 0) + vx = (int)(dx * sqrt(gy / (2.0 * tmp))); + else + vx = 0; + + // Restore the left/right direction: dx and vx should have the same sign. + if (dxWasNegative) + vx = -vx; + + if ((dy < 0) && (vx == 0)) { + // Special case: If this was a jump (almost) straight upward, i.e. dy < 0 (upward), + // and vx == 0 (i.e. no horizontal movement, at least not after rounding), then we + // compute vy directly. + // For this, we drop the assumption on the linear correlation of vx and vy (obviously). + + // FIXME: This choice of vy makes t roughly (2+sqrt(2))/gy * sqrt(dy); + // so if gy==3, then t is roughly sqrt(dy)... + vy = (int)sqrt(gy * abs(2 * dy)) + 1; + } else { + // As stated above, the vertical direction is correlated to the horizontal by the + // (non-zero) factor c. + // Strictly speaking, we should probably be using the value of vx *before* rounding + // it to an integer... Ah well + vy = c * vx; + } + + // Always force vy to be upwards + vy = -abs(vy); + + SCIkdebug(SCIkBRESEN, "SetJump for object at "PREG"\n", PRINT_REG(object)); + SCIkdebug(SCIkBRESEN, "xStep: %d, yStep: %d\n", vx, vy); + + PUT_SEL32V(object, xStep, vx); + PUT_SEL32V(object, yStep, vy); + + return s->r_acc; +} + +#define _K_BRESEN_AXIS_X 0 +#define _K_BRESEN_AXIS_Y 1 + +void +initialize_bresen(state_t *s, int funct_nr, int argc, reg_t *argv, reg_t mover, int step_factor, + int deltax, int deltay) +{ + reg_t client = GET_SEL32(mover, client); + int stepx = GET_SEL32SV(client, xStep) * step_factor; + int stepy = GET_SEL32SV(client, yStep) * step_factor; + int numsteps_x = stepx? (abs(deltax) + stepx-1) / stepx : 0; + int numsteps_y = stepy? (abs(deltay) + stepy-1) / stepy : 0; + int bdi, i1; + int numsteps; + int deltax_step; + int deltay_step; + + if (numsteps_x > numsteps_y) { + numsteps = numsteps_x; + deltax_step = (deltax < 0)? -stepx : stepx; + deltay_step = numsteps? deltay / numsteps : deltay; + } else { /* numsteps_x <= numsteps_y */ + numsteps = numsteps_y; + deltay_step = (deltay < 0)? -stepy : stepy; + deltax_step = numsteps? deltax / numsteps : deltax; + } + + /* if (abs(deltax) > abs(deltay)) {*/ /* Bresenham on y */ + if (numsteps_y < numsteps_x) { + + PUT_SEL32V(mover, b_xAxis, _K_BRESEN_AXIS_Y); + PUT_SEL32V(mover, b_incr, (deltay < 0)? -1 : 1); + /* + i1 = 2 * (abs(deltay) - abs(deltay_step * numsteps)) * abs(deltax_step); + bdi = -abs(deltax); + */ + i1 = 2*(abs(deltay) - abs(deltay_step * (numsteps - 1))) * abs(deltax_step); + bdi = -abs(deltax); + + } else { /* Bresenham on x */ + + PUT_SEL32V(mover, b_xAxis, _K_BRESEN_AXIS_X); + PUT_SEL32V(mover, b_incr, (deltax < 0)? -1 : 1); + /* + i1= 2 * (abs(deltax) - abs(deltax_step * numsteps)) * abs(deltay_step); + bdi = -abs(deltay); + */ + i1 = 2*(abs(deltax) - abs(deltax_step * (numsteps - 1))) * abs(deltay_step); + bdi = -abs(deltay); + + } + + PUT_SEL32V(mover, dx, deltax_step); + PUT_SEL32V(mover, dy, deltay_step); + + SCIkdebug(SCIkBRESEN, "Init bresen for mover "PREG": d=(%d,%d)\n", PRINT_REG(mover), deltax, deltay); + SCIkdebug(SCIkBRESEN, " steps=%d, mv=(%d, %d), i1= %d, i2=%d\n", + numsteps, deltax_step, deltay_step, i1, bdi*2); + +/* PUT_SEL32V(mover, b_movCnt, numsteps); *//* Needed for HQ1/Ogre? */ + PUT_SEL32V(mover, b_di, bdi); + PUT_SEL32V(mover, b_i1, i1); + PUT_SEL32V(mover, b_i2, bdi * 2); + +} + +reg_t +kInitBresen(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t mover = argv[0]; + reg_t client = GET_SEL32(mover, client); + + int deltax = GET_SEL32SV(mover, x) - GET_SEL32SV(client, x); + int deltay = GET_SEL32SV(mover, y) - GET_SEL32SV(client, y); + + initialize_bresen(s, funct_nr, argc, argv, mover, KP_UINT(KP_ALT(1, make_reg(0, 1))), deltax, deltay); + + return s->r_acc; +} + + +#define MOVING_ON_X (((axis == _K_BRESEN_AXIS_X)&&bi1) || dx) +#define MOVING_ON_Y (((axis == _K_BRESEN_AXIS_Y)&&bi1) || dy) + +static enum { + IGNORE_MOVECNT, + INCREMENT_MOVECNT, + UNINITIALIZED +} handle_movecnt = UNINITIALIZED; + +int parse_reg_t(state_t *s, char *str, reg_t *dest); /* In scriptconsole.c */ + +static int +checksum_bytes(byte *data, int size) +{ + int result = 0; + int i; + + for (i = 0; i < size; i++) + { + result += *data; + data++; + } + + return result; +} + +static void +bresenham_autodetect(state_t *s) +{ + reg_t motion_class; + + if (!parse_reg_t(s, "?Motion", &motion_class)) + { + object_t *obj = obj_get(s, motion_class); + reg_t fptr; + byte *buf; + + if (obj == NULL) + { + SCIkwarn(SCIkWARNING,"bresenham_autodetect failed!"); + handle_movecnt = INCREMENT_MOVECNT; /* Most games do this, so best guess */ + return; + } + + if (lookup_selector(s, motion_class, s->selector_map.doit, NULL, &fptr) != SELECTOR_METHOD) + { + SCIkwarn(SCIkWARNING,"bresenham_autodetect failed!"); + handle_movecnt = INCREMENT_MOVECNT; /* Most games do this, so best guess */ + return; + } + + buf = s->seg_manager.heap[fptr.segment]->data.script.buf + fptr.offset; + handle_movecnt = (SCI_VERSION_MAJOR(s->version) == 0 || + checksum_bytes(buf, 8) == 0x216) ? INCREMENT_MOVECNT : IGNORE_MOVECNT; + sciprintf("b-moveCnt action based on checksum: %s\n", handle_movecnt == IGNORE_MOVECNT ? + "ignore" : "increment"); + } else + { + SCIkwarn(SCIkWARNING,"bresenham_autodetect failed!"); + handle_movecnt = INCREMENT_MOVECNT; /* Most games do this, so best guess */ + } +} + +reg_t +kDoBresen(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t mover = argv[0]; + reg_t client = GET_SEL32(mover, client); + + int x = GET_SEL32SV(client, x); + int y = GET_SEL32SV(client, y); + int oldx, oldy, destx, desty, dx, dy, bdi, bi1, bi2, movcnt, bdelta, axis; + word signal = GET_SEL32V(client, signal); + int completed = 0; + int max_movcnt = GET_SEL32V(client, moveSpeed); + + if (SCI_VERSION_MAJOR(s->version)>0) + signal&=~_K_VIEW_SIG_FLAG_HIT_OBSTACLE; + + if (handle_movecnt == UNINITIALIZED) + bresenham_autodetect(s); + + PUT_SEL32(client, signal, make_reg(0, signal)); /* This is a NOP for SCI0 */ + oldx = x; + oldy = y; + destx = GET_SEL32SV(mover, x); + desty = GET_SEL32SV(mover, y); + dx = GET_SEL32SV(mover, dx); + dy = GET_SEL32SV(mover, dy); + bdi = GET_SEL32SV(mover, b_di); + bi1 = GET_SEL32SV(mover, b_i1); + bi2 = GET_SEL32SV(mover, b_i2); + movcnt = GET_SEL32V(mover, b_movCnt); + bdelta = GET_SEL32SV(mover, b_incr); + axis = GET_SEL32SV(mover, b_xAxis); + +// sciprintf("movecnt %d, move speed %d\n", movcnt, max_movcnt); + + if (handle_movecnt) + { + if (max_movcnt > movcnt) + { + ++movcnt; + PUT_SEL32V(mover, b_movCnt, movcnt); /* Needed for HQ1/Ogre? */ + return NULL_REG; + } + else + { + movcnt = 0; + PUT_SEL32V(mover, b_movCnt, movcnt); /* Needed for HQ1/Ogre? */ + } + } + + if ((bdi += bi1) > 0) { + bdi += bi2; + + if (axis == _K_BRESEN_AXIS_X) + dx += bdelta; + else + dy += bdelta; + } + + PUT_SEL32V(mover, b_di, bdi); + + x += dx; + y += dy; + + if ((MOVING_ON_X + && (((x < destx) && (oldx >= destx)) /* Moving left, exceeded? */ + || + ((x > destx) && (oldx <= destx)) /* Moving right, exceeded? */ + || + ((x == destx) && (abs(dx) > abs(dy))) /* Moving fast, reached? */ + /* Treat this last case specially- when doing sub-pixel movements + ** on the other axis, we could still be far away from the destination */ + ) + ) + || (MOVING_ON_Y + && (((y < desty) && (oldy >= desty)) /* Moving upwards, exceeded? */ + || + ((y > desty) && (oldy <= desty)) /* Moving downwards, exceeded? */ + || + ((y == desty) && (abs(dy) >= abs(dx))) /* Moving fast, reached? */ + ) + ) + ) + /* Whew... in short: If we have reached or passed our target position */ + { + x = destx; + y = desty; + completed = 1; + + SCIkdebug(SCIkBRESEN, "Finished mover "PREG"\n", PRINT_REG(mover)); + } + + + PUT_SEL32V(client, x, x); + PUT_SEL32V(client, y, y); + + SCIkdebug(SCIkBRESEN, "New data: (x,y)=(%d,%d), di=%d\n", x, y, bdi); + + if (s->version >= SCI_VERSION_FTU_INVERSE_CANBEHERE) + invoke_selector(INV_SEL(client, cantBeHere, 0), 0); else + invoke_selector(INV_SEL(client, canBeHere, 0), 0); + + s->r_acc = not_register(s, s->r_acc); + + if (!s->r_acc.offset) { /* Contains the return value */ + + signal = GET_SEL32V(client, signal); + + PUT_SEL32V(client, x, oldx); + PUT_SEL32V(client, y, oldy); + + PUT_SEL32V(client, signal, (signal | _K_VIEW_SIG_FLAG_HIT_OBSTACLE)); + + SCIkdebug(SCIkBRESEN, "Finished mover "PREG" by collision\n", PRINT_REG(mover)); + completed = 1; + } + + if (SCI_VERSION_MAJOR(s->version)>0) + if (completed) + invoke_selector(INV_SEL(mover, moveDone, 0), 0); + + return make_reg(0, completed); +} + +extern void +_k_dirloop(reg_t obj, word angle, state_t *s, int funct_nr, + int argc, reg_t *argv); +/* From kgraphics.c, used as alternative looper */ + +int +is_heap_object(state_t *s, reg_t pos); +/* From kscripts.c */ + +extern int +get_angle(int xrel, int yrel); +/* from kmath.c, used for calculating angles */ + + +reg_t +kDoAvoider(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t avoider = argv[0]; + reg_t client, looper, mover; + int angle; + int dx, dy; + int destx, desty; + + + s->r_acc = make_reg(0, -1); + + if (!is_heap_object(s, avoider)) { + SCIkwarn(SCIkWARNING, "DoAvoider() where avoider "PREG" is not an object\n", PRINT_REG(avoider)); + return NULL_REG; + } + + client = GET_SEL32(avoider, client); + + if (!is_heap_object(s, client)) { + SCIkwarn(SCIkWARNING, "DoAvoider() where client "PREG" is not an object\n", PRINT_REG(client)); + return NULL_REG; + } + + looper = GET_SEL32(client, looper); + + mover = GET_SEL32(client, mover); + + if (!is_heap_object(s, mover)) { + if (mover.segment) { + SCIkwarn(SCIkWARNING, "DoAvoider() where mover "PREG" is not an object\n", PRINT_REG(mover)); + } + return s->r_acc; + } + + destx = GET_SEL32V(mover, x); + desty = GET_SEL32V(mover, y); + + SCIkdebug(SCIkBRESEN, "Doing avoider %04x (dest=%d,%d)\n", avoider, destx, desty); + + if (invoke_selector(INV_SEL(mover, doit, 1) , 0)) { + SCIkwarn(SCIkERROR, "Mover "PREG" of avoider "PREG + " doesn't have a doit() funcselector\n", + PRINT_REG(mover), PRINT_REG(avoider)); + return NULL_REG; + } + + mover = GET_SEL32(client, mover); + if (!mover.segment) /* Mover has been disposed? */ + return s->r_acc; /* Return gracefully. */ + + if (invoke_selector(INV_SEL(client, isBlocked, 1) , 0)) { + SCIkwarn(SCIkERROR, "Client "PREG" of avoider "PREG" doesn't" + " have an isBlocked() funcselector\n", PRINT_REG(client), PRINT_REG(avoider)); + return NULL_REG; + } + + dx = destx - GET_SEL32V(client, x); + dy = desty - GET_SEL32V(client, y); + angle = get_angle(dx, dy); + + SCIkdebug(SCIkBRESEN, "Movement (%d,%d), angle %d is %sblocked\n", + dx, dy, angle, (s->r_acc.offset)? " ": "not "); + + if (s->r_acc.offset) { /* isBlocked() returned non-zero */ + int rotation = (rand() & 1)? 45 : (360-45); /* Clockwise/counterclockwise */ + int oldx = GET_SEL32V(client, x); + int oldy = GET_SEL32V(client, y); + int xstep = GET_SEL32V(client, xStep); + int ystep = GET_SEL32V(client, yStep); + int moves; + + SCIkdebug(SCIkBRESEN, " avoider "PREG"\n", PRINT_REG(avoider)); + + for (moves = 0; moves < 8; moves++) { + int move_x = (int) (sin(angle * PI / 180.0) * (xstep)); + int move_y = (int) (-cos(angle * PI / 180.0) * (ystep)); + + PUT_SEL32V(client, x, oldx + move_x); + PUT_SEL32V(client, y, oldy + move_y); + + SCIkdebug(SCIkBRESEN, "Pos (%d,%d): Trying angle %d; delta=(%d,%d)\n", + oldx, oldy, angle, move_x, move_y); + + if (invoke_selector(INV_SEL(client, canBeHere, 1) , 0)) { + SCIkwarn(SCIkERROR, "Client "PREG" of avoider "PREG" doesn't" + " have a canBeHere() funcselector\n", + PRINT_REG(client), PRINT_REG(avoider)); + return NULL_REG; + } + + PUT_SEL32V(client, x, oldx); + PUT_SEL32V(client, y, oldy); + + if (s->r_acc.offset) { /* We can be here */ + SCIkdebug(SCIkBRESEN, "Success\n"); + PUT_SEL32V(client, heading, angle); + + return make_reg(0, angle); + } + + angle += rotation; + + if (angle > 360) + angle -= 360; + } + + SCIkwarn(SCIkWARNING, "DoAvoider failed for avoider "PREG"\n", + PRINT_REG(avoider)); + + } else { + int heading = GET_SEL32V(client, heading); + + if (heading == -1) + return s->r_acc; /* No change */ + + PUT_SEL32V(client, heading, angle); + + s->r_acc = make_reg(0, angle); + + if (looper.segment) { + if (invoke_selector(INV_SEL(looper, doit, 1), 2, angle, client)) { + SCIkwarn(SCIkERROR, "Looper "PREG" of avoider "PREG" doesn't" + " have a doit() funcselector\n", + PRINT_REG(looper), PRINT_REG(avoider)); + } else return s->r_acc; + } else + /* No looper? Fall back to DirLoop */ + + _k_dirloop(client, (word)angle, s, funct_nr, argc, argv); + } + + return s->r_acc; +} + diff --git a/engines/sci/engine/kpathing.c b/engines/sci/engine/kpathing.c new file mode 100644 index 0000000000..aab4986462 --- /dev/null +++ b/engines/sci/engine/kpathing.c @@ -0,0 +1,1734 @@ +/*************************************************************************** + kpathing.c Copyright (C) 2002-2006 Lars Skovlund, Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik [w.f.b.w.v.niftrik@stud.tue.nl] + +***************************************************************************/ + +/* Detailed information on the implementation can be found in the report +** which can be downloaded from FIXME. +*/ + +#include + +#include +#include +#include + +#define POLY_LAST_POINT 0x7777 +#define POLY_POINT_SIZE 4 + +#define POLY_GET_POINT(p, i, x, y) do { x = getInt16((p) + (i) * POLY_POINT_SIZE); \ + y = getInt16((p) + 2 + (i) * POLY_POINT_SIZE); \ +} while (0) +#define POLY_SET_POINT(p, i, x, y) do { putInt16((p) + (i) * POLY_POINT_SIZE, x); \ + putInt16((p) + 2 + (i) * POLY_POINT_SIZE, y); \ +} while (0) +#define POLY_GET_POINT_REG_T(p, i, x, y) do { x = KP_SINT((p)[(i) * 2]); \ + y = KP_SINT((p)[(i) * 2 + 1]); \ +} while (0) + +/* SCI-defined polygon types */ +#define POLY_TOTAL_ACCESS 0 +#define POLY_NEAREST_ACCESS 1 +#define POLY_BARRED_ACCESS 2 +#define POLY_CONTAINED_ACCESS 3 + +/* Polygon containment types */ +#define CONT_OUTSIDE 0 +#define CONT_ON_EDGE 1 +#define CONT_INSIDE 2 + +#define POINT_EQUAL(A, B) (((A).x == (B).x) && ((A).y == (B).y)) + +#define HUGE_DISTANCE 1e10 + +/* Visibility matrix */ +#define VIS_MATRIX_ROW_SIZE(N) (((N) / 8) + ((N) % 8 ? 1 : 0)) +#define SET_VISIBLE(S, P, Q) ((S)->vis_matrix)[(P) * VIS_MATRIX_ROW_SIZE((S)->vertices) \ + + (Q) / 8] |= (1 << ((Q) % 8)) +#define IS_VISIBLE(S, P, Q) (((S)->vis_matrix[(P) * VIS_MATRIX_ROW_SIZE((S)->vertices) \ + + (Q) / 8] & (1 << ((Q) % 8))) != 0) + +#define VERTEX_HAS_EDGES(V) ((V) != CLIST_NEXT(V, entries)) + +/* Error codes */ +#define PF_OK 0 +#define PF_ERROR -1 +#define PF_FATAL -2 + +/* Floating point struct */ +typedef struct pointf +{ + float x, y; +} pointf_t; + +pointf_t +pointf(float x, float y) +{ + pointf_t p; + + p.x = x; + p.y = y; + + return p; +} + +pointf_t +to_pointf(point_t p) +{ + return pointf(p.x, p.y); +} + +typedef struct vertex +{ + /* Location */ + point_t v; + + /* Index */ + int idx; + + /* Vertex list entry */ + CLIST_ENTRY(vertex) entries; + + /* Dijkstra list entry */ + LIST_ENTRY(vertex) dijkstra; + + /* Distance from starting vertex */ + float dist; + + /* Previous vertex in shortest path */ + struct vertex *path_prev; +} vertex_t; + +typedef CLIST_HEAD(vertices_head, vertex) vertices_head_t; + +typedef struct polygon +{ + /* Circular list of vertices */ + vertices_head_t vertices; + + /* Polygon list entry */ + LIST_ENTRY(polygon) entries; + + /* SCI polygon type */ + int type; +} polygon_t; + +/* Pathfinding state */ +typedef struct pf_state +{ + /* List of all polygons */ + LIST_HEAD(polygons_head, polygon) polygons; + + /* Original start and end points */ + point_t start, end; + + /* Flags for adding original points to final path */ + char keep_start, keep_end; + + /* Start and end points for pathfinding */ + vertex_t *vertex_start, *vertex_end; + + /* Array to quickly find vertices by idx */ + vertex_t **vertex_index; + + /* Visibility matrix */ + char *vis_matrix; + + /* Total number of vertices */ + int vertices; +} pf_state_t; + +static vertex_t *vertex_cur; + +/* Temporary hack to deal with points in reg_ts */ +static int +polygon_is_reg_t(unsigned char *list, int size) +{ + int i; + + /* Check the first three reg_ts */ + for (i = 0; i < (size < 3 ? size : 3); i++) + if ((((reg_t *) list) + i)->segment) + /* Non-zero segment, cannot be reg_ts */ + return 0; + + /* First three segments were zero, assume reg_ts */ + return 1; +} + +static point_t +read_point(unsigned char *list, int is_reg_t, int offset) +{ + point_t point; + + if (!is_reg_t) { + POLY_GET_POINT(list, offset, point.x, point.y); + } else { + POLY_GET_POINT_REG_T((reg_t *) list, offset, point.x, point.y); + } + + return point; +} + + + /*** Debug functions ***/ + +static void +draw_line(state_t *s, point_t p1, point_t p2, int type) +{ + /* Colors for polygon debugging. + ** Green: Total access + ** Red : Barred access + ** Blue: Near-point access + ** Yellow: Contained access + */ + int poly_colors[][3] = {{0, 255, 0}, {0, 0, 255}, {255, 0, 0}, {255, 255, 0}}; + gfx_color_t col; + gfxw_list_t *decorations = s->picture_port->decorations; + gfxw_primitive_t *line; + + col.visual.global_index = GFX_COLOR_INDEX_UNMAPPED; + col.visual.r = poly_colors[type][0]; + col.visual.g = poly_colors[type][1]; + col.visual.b = poly_colors[type][2]; + col.alpha = 0; + col.priority = -1; + col.control = 0; + col.mask = GFX_MASK_VISUAL | GFX_MASK_PRIORITY; + + p1.y += 10; + p2.y += 10; + + line = gfxw_new_line(p1, p2, col, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL); + decorations->add((gfxw_container_t *) decorations, (gfxw_widget_t *) line); +} + +static void +draw_point(state_t *s, point_t p, int start) +{ + /* Colors for starting and end point + ** Green: End point + ** Blue: Starting point + */ + int point_colors[][3] = {{0, 255, 0}, {0, 0, 255}}; + gfx_color_t col; + gfxw_list_t *decorations = s->picture_port->decorations; + gfxw_box_t *box; + + col.visual.global_index = GFX_COLOR_INDEX_UNMAPPED; + col.visual.r = point_colors[start][0]; + col.visual.g = point_colors[start][1]; + col.visual.b = point_colors[start][2]; + col.alpha = 0; + col.priority = -1; + col.control = 0; + col.mask = GFX_MASK_VISUAL | GFX_MASK_PRIORITY; + + box = gfxw_new_box(s->gfx_state, gfx_rect(p.x - 1, p.y - 1 + 10, 3, 3), col, col, GFX_BOX_SHADE_FLAT); + decorations->add((gfxw_container_t *) decorations, (gfxw_widget_t *) box); +} + +static void +draw_polygon(state_t *s, reg_t polygon) +{ + reg_t points = GET_SEL32(polygon, points); + int size = KP_UINT(GET_SEL32(polygon, size)); + int type = KP_UINT(GET_SEL32(polygon, type)); + point_t first, prev; + unsigned char *list = kernel_dereference_bulk_pointer(s, points, size * POLY_POINT_SIZE); + int is_reg_t = polygon_is_reg_t(list, size); + int i; + + prev = first = read_point(list, is_reg_t, 0); + + for (i = 1; i < size; i++) { + point_t point = read_point(list, is_reg_t, i); + draw_line(s, prev, point, type); + prev = point; + } + + draw_line(s, prev, first, type % 3); +} + +static void +draw_input(state_t *s, reg_t poly_list, point_t start, point_t end, int opt) +{ + list_t *list; + node_t *node; + + draw_point(s, start, 1); + draw_point(s, end, 0); + + if (!poly_list.segment) + return; + + list = LOOKUP_LIST(poly_list); + + if (!list) { + SCIkwarn(SCIkWARNING, "Could not obtain polygon list\n"); + return; + } + + node = LOOKUP_NODE(list->first); + + while (node) { + draw_polygon(s, node->value); + node = LOOKUP_NODE(node->succ); + } +} + +static void +print_polygon(state_t *s, reg_t polygon) +{ + reg_t points = GET_SEL32(polygon, points); + int size = KP_UINT(GET_SEL32(polygon, size)); + int type = KP_UINT(GET_SEL32(polygon, type)); + int i; + unsigned char *point_array = kernel_dereference_bulk_pointer(s, points, size * POLY_POINT_SIZE); + int is_reg_t = polygon_is_reg_t(point_array, size); + point_t point; + + sciprintf("%i:", type); + + for (i = 0; i < size; i++) { + point = read_point(point_array, is_reg_t, i); + sciprintf(" (%i, %i)", point.x, point.y); + } + + point = read_point(point_array, is_reg_t, 0); + sciprintf(" (%i, %i);\n", point.x, point.y); +} + +static void +print_input(state_t *s, reg_t poly_list, point_t start, point_t end, int opt) +{ + list_t *list; + node_t *node; + + sciprintf("Start point: (%i, %i)\n", start.x, start.y); + sciprintf("End point: (%i, %i)\n", end.x, end.y); + sciprintf("Optimization level: %i\n", opt); + + if (!poly_list.segment) + return; + + list = LOOKUP_LIST(poly_list); + + if (!list) { + SCIkwarn(SCIkWARNING, "Could not obtain polygon list\n"); + return; + } + + sciprintf("Polygons:\n"); + node = LOOKUP_NODE(list->first); + + while (node) { + print_polygon(s, node->value); + node = LOOKUP_NODE(node->succ); + } +} + + + /*** Basic geometry functions ***/ + +static int +area(point_t a, point_t b, point_t c) +/* Computes the area of a triangle +** Parameters: (point_t) a, b, c: The points of the triangle +** Returns : (int) The area multiplied by two +*/ +{ + return (b.x - a.x) * (a.y - c.y) - (c.x - a.x) * (a.y - b.y); +} + +static int +left(point_t a, point_t b, point_t c) +/* Determines whether or not a point is to the left of a directed line +** Parameters: (point_t) a, b: The directed line (a, b) +** (point_t) c: The query point +** Returns : (int) 1 if c is to the left of (a, b), 0 otherwise +*/ +{ + return area(a, b, c) > 0; +} + +static int +left_on(point_t a, point_t b, point_t c) +/* Determines whether or not a point is to the left of or collinear with a +** directed line +** Parameters: (point_t) a, b: The directed line (a, b) +** (point_t) c: The query point +** Returns : (int) 1 if c is to the left of or collinear with (a, b), 0 +** otherwise +*/ +{ + return area(a, b, c) >= 0; +} + +static int +collinear(point_t a, point_t b, point_t c) +/* Determines whether or not three points are collinear +** Parameters: (point_t) a, b, c: The three points +** Returns : (int) 1 if a, b, and c are collinear, 0 otherwise +*/ +{ + return area(a, b, c) == 0; +} + +static int +between(point_t a, point_t b, point_t c) +/* Determines whether or not a point lies on a line segment +** Parameters: (point_t) a, b: The line segment (a, b) +** (point_t) c: The query point +** Returns : (int) 1 if c lies on (a, b), 0 otherwise +*/ +{ + if (!collinear(a, b, c)) + return 0; + + /* Assumes a != b. */ + 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 int +intersect_proper(point_t a, point_t b, point_t c, point_t d) +/* Determines whether or not two line segments properly intersect +** Parameters: (point_t) a, b: The line segment (a, b) +** (point_t) c, d: The line segment (c, d) +** Returns : (int) 1 if (a, b) properly intersects (c, d), 0 otherwise +*/ +{ + int ab = (left(a, b, c) && left(b, a, d)) + || (left(a, b, d) && left(b, a, c)); + int cd = (left(c, d, a) && left(d, c, b)) + || (left(c, d, b) && left(d, c, a)); + + return ab && cd; +} + +static int +intersect(point_t a, point_t b, point_t c, point_t d) +/* Determines whether or not two line segments intersect +** Parameters: (point_t) a, b: The line segment (a, b) +** (point_t) c, d: The line segment (c, d) +** Returns : (int) 1 if (a, b) intersects (c, d), 0 otherwise +*/ +{ + if (intersect_proper(a, b, c, d)) + return 1; + + return between(a, b, c) || between(a, b, d) + || between (c, d, a) || between(c, d, b); +} + + + /*** Pathfinding ***/ + +static vertex_t * +vertex_new(point_t p) +/* Allocates and initialises a new vertex +** Parameters: (point_t) p: The position of the vertex +** Returns : (vertex_t *) A newly allocated vertex +*/ +{ + vertex_t *vertex = (vertex_t*)sci_malloc(sizeof(vertex_t)); + + vertex->v = p; + vertex->dist = HUGE_DISTANCE; + vertex->path_prev = NULL; + + return vertex; +} + +static polygon_t * +polygon_new(int type) +/* Allocates and initialises a new polygon +** Parameters: (int) type: The SCI polygon type +** Returns : (polygon_t *) A newly allocated polygon +*/ +{ + polygon_t *polygon = (polygon_t*)sci_malloc(sizeof(polygon_t)); + + CLIST_INIT(&polygon->vertices); + polygon->type = type; + + return polygon; +} + +static int +contained(point_t p, polygon_t *polygon) +/* Polygon containment test +** Parameters: (point_t) p: The point +** (polygon_t *) polygon: The polygon +** Returns : (int) CONT_INSIDE if p is strictly contained in polygon, +** CONT_ON_EDGE if p lies on an edge of polygon, +** CONT_OUTSIDE otherwise +*/ +{ + /* Number of ray crossing left and right */ + int lcross = 0, rcross = 0; + vertex_t *vertex; + + /* Iterate over edges */ + CLIST_FOREACH(vertex, &polygon->vertices, entries) { + point_t v1 = vertex->v; + point_t v2 = CLIST_NEXT(vertex, entries)->v; + + /* Flags for ray straddling left and right */ + int rstrad, lstrad; + + /* Check if p is a vertex */ + if (POINT_EQUAL(p, v1)) + return CONT_ON_EDGE; + + /* Check if edge straddles the ray */ + rstrad = (v1.y < p.y) != (v2.y < p.y); + lstrad = (v1.y > p.y) != (v2.y > p.y); + + if (lstrad || rstrad) { + /* Compute intersection point x / xq */ + int x = v2.x * v1.y - v1.x * v2.y + (v1.x - v2.x) * p.y; + int xq = v1.y - v2.y; + + /* Multiply by -1 if xq is negative (for comparison + ** that follows) + */ + if (xq < 0) { + x = -x; + xq = -xq; + } + + /* Avoid floats by multiplying instead of dividing */ + if (rstrad && (x > xq * p.x)) + rcross++; + else if (lstrad && (x < xq * p.x)) + lcross++; + } + } + + /* If we counted an odd number of total crossings the point is on an + ** edge + */ + if ((lcross + rcross) % 2 == 1) + return CONT_ON_EDGE; + + /* If there are an odd number of crossings to one side the point is + ** contained in the polygon + */ + if (rcross % 2 == 1) { + /* Invert result for contained access polygons. */ + if (polygon->type == POLY_CONTAINED_ACCESS) + return CONT_OUTSIDE; + return CONT_INSIDE; + } + + /* Point is outside polygon. Invert result for contained access + ** polygons + */ + if (polygon->type == POLY_CONTAINED_ACCESS) + return CONT_INSIDE; + + return CONT_OUTSIDE; +} + +static int +polygon_area(polygon_t *polygon) +/* Computes polygon area +** Parameters: (polygon_t *) polygon: The polygon +** Returns : (int) The area multiplied by two +*/ +{ + vertex_t *first = CLIST_FIRST(&polygon->vertices); + vertex_t *v; + int size = 0; + + v = CLIST_NEXT(first, entries); + + while (CLIST_NEXT(v, entries) != first) { + size += area(first->v, v->v, CLIST_NEXT(v, entries)->v); + v = CLIST_NEXT(v, entries); + } + + return size; +} + +static void +fix_vertex_order(polygon_t *polygon) +/* Fixes the vertex order of a polygon if incorrect. Contained access +** polygons should have their vertices ordered clockwise, all other types +** anti-clockwise +** Parameters: (polygon_t *) polygon: The polygon +** Returns : (void) +*/ +{ + int area = polygon_area(polygon); + + /* When the polygon area is positive the vertices are ordered + ** anti-clockwise. When the area is negative the vertices are ordered + ** clockwise + */ + if (((area > 0) && (polygon->type == POLY_CONTAINED_ACCESS)) + || ((area < 0) && (polygon->type != POLY_CONTAINED_ACCESS))) { + vertices_head_t vertices; + + /* Create a new circular list */ + CLIST_INIT(&vertices); + + while (!CLIST_EMPTY(&polygon->vertices)) { + /* Put first vertex in new list */ + vertex_t *vertex = CLIST_FIRST(&polygon->vertices); + CLIST_REMOVE(&polygon->vertices, vertex, entries); + CLIST_INSERT_HEAD(&vertices, vertex, entries); + } + + polygon->vertices = vertices; + } +} + +static int +vertex_compare(const void *a, const void *b) +/* Compares two vertices by angle (first) and distance (second) in relation +** to vertex_cur. The angle is relative to the horizontal line extending +** right from vertex_cur, and increases clockwise +** Parameters: (const void *) a, b: The vertices +** Returns : (int) -1 if a is smaller than b, 1 if a is larger than b, and +** 0 if a and b are equal +*/ +{ + point_t p0 = vertex_cur->v; + point_t p1 = (*(vertex_t **) a)->v; + point_t p2 = (*(vertex_t **) b)->v; + + if (POINT_EQUAL(p1, p2)) + return 0; + + /* Points above p0 have larger angle than points below p0 */ + if ((p1.y < p0.y) && (p2.y >= p0.y)) + return 1; + + if ((p2.y < p0.y) && (p1.y >= p0.y)) + return -1; + + /* Handle case where all points have the same y coordinate */ + if ((p0.y == p1.y) && (p0.y == p2.y)) { + /* Points left of p0 have larger angle than points right of + ** p0 + */ + if ((p1.x < p0.x) && (p2.x >= p0.x)) + return 1; + if ((p1.x >= p0.x) && (p2.x < p0.x)) + return -1; + } + + if (collinear(p0, p1, p2)) { + /* At this point collinear points must have the same angle, + ** so compare distance to p0 + */ + if (abs(p1.x - p0.x) < abs(p2.x - p0.x)) + return -1; + if (abs(p1.y - p0.y) < abs(p2.y - p0.y)) + return -1; + + return 1; + } + + /* If p2 is left of the directed line (p0, p1) then p1 has greater + ** angle + */ + if (left(p0, p1, p2)) + return 1; + + return -1; +} + +static void +clockwise(vertex_t *v, point_t *p1, point_t *p2) +/* Orders the points of an edge clockwise around vertex_cur. If all three +** points are collinear the original order is used +** Parameters: (vertex_t *) v: The first vertex of the edge +** Returns : (void) +** (point_t) *p1: The first point in clockwise order +** (point_t) *p2: The second point in clockwise order +*/ +{ + vertex_t *w = CLIST_NEXT(v, entries); + + if (left_on(vertex_cur->v, w->v, v->v)) { + *p1 = v->v; + *p2 = w->v; + return; + } + + *p1 = w->v; + *p2 = v->v; + return; +} + +static int +edge_compare(const void *a, const void *b) +/* Compares two edges that are intersected by the sweeping line by distance +** from vertex_cur +** Parameters: (const void *) a, b: The first vertices of the edges +** Returns : (int) -1 if a is closer than b, 1 if b is closer than a, and +** 0 if a and b are equal +*/ +{ + point_t v1, v2, w1, w2; + + /* We can assume that the sweeping line intersects both edges and + ** that the edges do not properly intersect + */ + + if (a == b) + return 0; + + /* Order vertices clockwise so we know vertex_cur is to the right of + ** directed edges (v1, v2) and (w1, w2) + */ + clockwise((vertex_t *) a, &v1, &v2); + clockwise((vertex_t *) b, &w1, &w2); + + /* As the edges do not properly intersect one edge must lie entirely + ** to one side of another. Note that the special case where edges are + ** collinear does not need to be handled as those edges will never be + ** in the tree simultaneously + */ + + /* b is left of a */ + if (left_on(v1, v2, w1) && left_on(v1, v2, w2)) + return -1; + + /* b is right of a */ + if (left_on(v2, v1, w1) && left_on(v2, v1, w2)) + return 1; + + /* a is left of b */ + if (left_on(w1, w2, v1) && left_on(w1, w2, v2)) + return 1; + + /* a is right of b */ + return -1; +} + +static int +inside(point_t p, vertex_t *vertex) +/* Determines whether or not a line from a point to a vertex intersects the +** interior of the polygon, locally at that vertex +** Parameters: (point_t) p: The point +** (vertex_t *) vertex: The vertex +** Returns : (int) 1 if the line (p, vertex->v) intersects the interior of +** the polygon, locally at the vertex. 0 otherwise +*/ +{ + /* Check that it's not a single-vertex polygon */ + if (VERTEX_HAS_EDGES(vertex)) { + point_t prev = CLIST_PREV(vertex, entries)->v; + point_t next = CLIST_NEXT(vertex, entries)->v; + point_t cur = vertex->v; + + if (left(prev, cur, next)) { + /* Convex vertex, line (p, cur) intersects the inside + ** if p is located left of both edges + */ + if (left(cur, next, p) && left(prev, cur, p)) + return 1; + } else { + /* Non-convex vertex, line (p, cur) intersects the + ** inside if p is located left of either edge + */ + if (left(cur, next, p) || left(prev, cur, p)) + return 1; + } + } + + return 0; +} + +static int +visible(vertex_t *vertex, vertex_t *vertex_prev, int visible, aatree_t *tree) +/* Determines whether or not a vertex is visible from vertex_cur +** Parameters: (vertex_t *) vertex: The vertex +** (vertex_t *) vertex_prev: The previous vertex in the sort +** order, or NULL +** (int) visible: 1 if vertex_prev is visible, 0 otherwise +** (aatree_t *) tree: The tree of edges intersected by the +** sweeping line +** Returns : (int) 1 if vertex is visible from vertex_cur, 0 otherwise +*/ +{ + vertex_t *edge; + point_t p = vertex_cur->v; + point_t w = vertex->v; + aatree_t *tree_n = tree; + + /* Check if sweeping line intersects the interior of the polygon + ** locally at vertex + */ + if (inside(p, vertex)) + return 0; + + /* If vertex_prev is on the sweeping line, then vertex is invisible + ** if vertex_prev is invisible + */ + if (vertex_prev && !visible && between(p, w, vertex_prev->v)) + return 0; + + /* Find leftmost node of tree */ + while ((tree_n = aatree_walk(tree_n, AATREE_WALK_LEFT))) + tree = tree_n; + edge = (vertex_t*)aatree_get_data(tree); + + if (edge) { + point_t p1, p2; + + /* Check for intersection with sweeping line before vertex */ + clockwise(edge, &p1, &p2); + if (left(p2, p1, p) && left(p1, p2, w)) + return 0; + } + + return 1; +} + +static void +visible_vertices(pf_state_t *s, vertex_t *vert) +/* Determines all vertices that are visible from a particular vertex and +** updates the visibility matrix +** Parameters: (pf_state_t *) s: The pathfinding state +** (vertex_t *) vert: The vertex +** Returns : (void) +*/ +{ + aatree_t *tree = aatree_new(); + point_t p = vert->v; + polygon_t *polygon; + int i; + int is_visible; + vertex_t **vert_sorted = (vertex_t**)sci_malloc(sizeof(vertex_t *) * s->vertices); + + /* Sort vertices by angle (first) and distance (second) */ + memcpy(vert_sorted, s->vertex_index, sizeof(vertex_t *) * s->vertices); + vertex_cur = vert; + qsort(vert_sorted, s->vertices, sizeof(vertex_t *), vertex_compare); + + LIST_FOREACH(polygon, &s->polygons, entries) { + vertex_t *vertex; + + vertex = CLIST_FIRST(&polygon->vertices); + + /* Check that there is more than one vertex. */ + if (VERTEX_HAS_EDGES(vertex)) + CLIST_FOREACH(vertex, &polygon->vertices, entries) { + point_t high, low; + + /* Add edges that intersect the initial position of the sweeping line */ + clockwise(vertex, &high, &low); + + if ((high.y < p.y) && (low.y >= p.y) && !POINT_EQUAL(low, p)) + aatree_insert(vertex, &tree, edge_compare); + } + } + + is_visible = 1; + + /* The first vertex will be vertex_cur, so we skip it */ + for (i = 1; i < s->vertices; i++) { + vertex_t *v1; + + /* Compute visibility of vertex_index[i] */ + is_visible = visible(vert_sorted[i], vert_sorted[i - 1], is_visible, tree); + + /* Update visibility matrix */ + if (is_visible) + SET_VISIBLE(s, vert->idx, vert_sorted[i]->idx); + + /* Delete anti-clockwise edges from tree */ + v1 = CLIST_PREV(vert_sorted[i], entries); + if (left(p, vert_sorted[i]->v, v1->v)) { + if (aatree_delete(v1, &tree, edge_compare)) + sciprintf("[avoidpath] Error: failed to remove edge from tree\n"); + } + + v1 = CLIST_NEXT(vert_sorted[i], entries); + if (left(p, vert_sorted[i]->v, v1->v)) { + if (aatree_delete(vert_sorted[i], &tree, edge_compare)) + sciprintf("[avoidpath] Error: failed to remove edge from tree\n"); + } + + /* Add clockwise edges of collinear vertices when sweeping line moves */ + if ((i < s->vertices - 1) && !collinear(p, vert_sorted[i]->v, vert_sorted[i + 1]->v)) { + int j; + for (j = i; (j >= 1) && collinear(p, vert_sorted[i]->v, vert_sorted[j]->v); j--) { + v1 = CLIST_PREV(vert_sorted[j], entries); + if (left(vert_sorted[j]->v, p, v1->v)) + aatree_insert(v1, &tree, edge_compare); + + v1 = CLIST_NEXT(vert_sorted[j], entries); + if (left(vert_sorted[j]->v, p, v1->v)) + aatree_insert(vert_sorted[j], &tree, edge_compare); + } + } + } + + sci_free(vert_sorted); + + /* Free tree */ + aatree_free(tree); +} + +static float +distance(pointf_t a, pointf_t b) +/* Computes the distance between two pointfs +** Parameters: (point_t) a, b: The two pointfs +** Returns : (int) The distance between a and b, rounded to int +*/ +{ + float w = a.x - b.x; + float h = a.y - b.y; + + return sqrt(w * w + h * h); +} + +static int +point_on_screen_border(point_t p) +/* Determines if a point lies on the screen border +** Parameters: (point_t) p: The point +** Returns : (int) 1 if p lies on the screen border, 0 otherwise +*/ +{ + /* FIXME get dimensions from somewhere? */ + return (p.x == 0) || (p.x == 319) || (p.y == 0) || (p.y == 189); +} + +static int +edge_on_screen_border(point_t p, point_t q) +/* Determines if an edge lies on the screen border +** Parameters: (point_t) p, q: The edge (p, q) +** Returns : (int) 1 if (p, q) lies on the screen border, 0 otherwise +*/ +{ + /* FIXME get dimensions from somewhere? */ + return ((p.x == 0 && q.x == 0) + || (p.x == 319 && q.x == 319) + || (p.y == 0 && q.y == 0) + || (p.y == 189 && q.y == 189)); +} + +static int +find_free_point(pointf_t f, polygon_t *polygon, point_t *ret) +/* Searches for a nearby point that is not contained in a polygon +** Parameters: (pointf_t) f: The pointf to search nearby +** (polygon_t *) polygon: The polygon +** Returns : (int) PF_OK on success, PF_FATAL otherwise +** (point_t) *ret: The non-contained point on success +*/ +{ + point_t p; + + /* Try nearest point first */ + p = gfx_point((int) floor(f.x + 0.5), + (int) floor(f.y + 0.5)); + + if (contained(p, polygon) != CONT_INSIDE) { + *ret = p; + return PF_OK; + } + + p = gfx_point((int) floor(f.x), + (int) floor(f.y)); + + /* Try (x, y), (x + 1, y), (x , y + 1) and (x + 1, y + 1) */ + if (contained(p, polygon) == CONT_INSIDE) { + p.x++; + if (contained(p, polygon) == CONT_INSIDE) { + p.y++; + if (contained(p, polygon) == CONT_INSIDE) { + p.x--; + if (contained(p, polygon) == CONT_INSIDE) + return PF_FATAL; + } + } + } + + *ret = p; + return PF_OK; +} + +static int +near_point(point_t p, polygon_t *polygon, point_t *ret) +/* Computes the near point of a point contained in a polygon +** Parameters: (point_t) p: The point +** (polygon_t *) polygon: The polygon +** Returns : (int) PF_OK on success, PF_FATAL otherwise +** (point_t) *ret: The near point of p in polygon on success +*/ +{ + vertex_t *vertex; + pointf_t near_p; + float dist = HUGE_DISTANCE; + + CLIST_FOREACH(vertex, &polygon->vertices, entries) { + point_t p1 = vertex->v; + point_t p2 = CLIST_NEXT(vertex, entries)->v; + float w, h, l, u; + pointf_t new_point; + float new_dist; + + /* Ignore edges on the screen border */ + if (edge_on_screen_border(p1, p2)) + continue; + + /* Compute near point */ + w = p2.x - p1.x; + h = p2.y - p1.y; + l = sqrt(w * w + h * h); + u = ((p.x - p1.x) * (p2.x - p1.x) + (p.y - p1.y) * (p2.y - p1.y)) / (l * l); + + /* Clip to edge */ + if (u < 0.0f) + u = 0.0f; + if (u > 1.0f) + u = 1.0f; + + new_point.x = p1.x + u * (p2.x - p1.x); + new_point.y = p1.y + u * (p2.y - p1.y); + + new_dist = distance(to_pointf(p), new_point); + + if (new_dist < dist) { + near_p = new_point; + dist = new_dist; + } + } + + /* Find point not contained in polygon */ + return find_free_point(near_p, polygon, ret); +} + +static int +intersection(point_t a, point_t b, vertex_t *vertex, pointf_t *ret) +/* Computes the intersection point of a line segment and an edge (not +** including the vertices themselves) +** Parameters: (point_t) a, b: The line segment (a, b) +** (vertex_t *) vertex: The first vertex of the edge +** Returns : (int) FP_OK on success, PF_ERROR otherwise +** (pointf_t) *ret: The intersection point +*/ +{ + /* Parameters of parametric equations */ + float s, t; + /* Numerator and denominator of equations */ + float num, denom; + point_t c = vertex->v; + point_t d = CLIST_NEXT(vertex, entries)->v; + + denom = a.x * (float) (d.y - c.y) + + b.x * (float) (c.y - d.y) + + d.x * (float) (b.y - a.y) + + c.x * (float) (a.y - b.y); + + if (denom == 0.0) + /* Segments are parallel, no intersection */ + return PF_ERROR; + + num = a.x * (float) (d.y - c.y) + + c.x * (float) (a.y - d.y) + + d.x * (float) (c.y - a.y); + + s = num / denom; + + num = -(a.x * (float) (c.y - b.y) + + b.x * (float) (a.y - c.y) + + c.x * (float) (b.y - a.y)); + + t = num / denom; + + if ((0.0 <= s) && (s <= 1.0) && (0.0 < t) && (t < 1.0)) { + /* Intersection found */ + ret->x = a.x + s * (b.x - a.x); + ret->y = a.y + s * (b.y - a.y); + return PF_OK; + } + + return PF_ERROR; +} + +static int +nearest_intersection(pf_state_t *s, point_t p, point_t q, point_t *ret) +/* Computes the nearest intersection point of a line segment and the polygon +** set. Intersection points that are reached from the inside of a polygon +** are ignored as are improper intersections which do not obstruct +** visibility +** Parameters: (pf_state_t *) s: The pathfinding state +** (point_t) p, q: The line segment (p, q) +** Returns : (int) PF_OK on success, PF_ERROR when no intersections were +** found, PF_FATAL otherwise +** (point_t) *ret: On success, the closest intersection point +*/ +{ + polygon_t *polygon; + pointf_t isec; + polygon_t *ipolygon; + float dist = HUGE_DISTANCE; + + LIST_FOREACH(polygon, &s->polygons, entries) { + vertex_t *vertex; + + CLIST_FOREACH(vertex, &polygon->vertices, entries) { + float new_dist; + pointf_t new_isec; + + /* Check for intersection with vertex */ + if (between(p, q, vertex->v)) { + /* Skip this vertex if we hit it from the + ** inside of the polygon + */ + if (inside(q, vertex)) { + new_isec.x = vertex->v.x; + new_isec.y = vertex->v.y; + } else + continue; + } else { + /* Check for intersection with edges */ + + /* Skip this edge if we hit it from the + ** inside of the polygon + */ + if (!left(vertex->v, CLIST_NEXT(vertex, entries)->v, q)) + continue; + + if (intersection(p, q, vertex, &new_isec) != PF_OK) + continue; + } + + new_dist = distance(to_pointf(p), new_isec); + if (new_dist < dist) { + ipolygon = polygon; + isec = new_isec; + dist = new_dist; + } + } + } + + if (dist == HUGE_DISTANCE) + return PF_ERROR; + + /* Find point not contained in polygon */ + return find_free_point(isec, ipolygon, ret); +} + +static int +fix_point(pf_state_t *s, point_t p, point_t *ret, polygon_t **ret_pol) +/* Checks a point for containment in any of the polygons in the polygon set. +** If the point is contained in a totally accessible polygon that polygon +** is removed from the set. If the point is contained in a polygon of another +** type the near point is returned. Otherwise the original point is returned +** Parameters: (point_t) p: The point +** Returns : (int) PF_OK on success, PF_FATAL otherwise +** (point_t) *ret: A valid input point for pathfinding +** (polygon_t *) *ret_pol: The polygon p was contained in if p +** != *ret, NULL otherwise +*/ +{ + polygon_t *polygon; + *ret_pol = NULL; + + /* Check for polygon containment */ + LIST_FOREACH(polygon, &s->polygons, entries) { + if (contained(p, polygon) != CONT_OUTSIDE) + break; + } + + if (polygon) { + point_t near_p; + + if (polygon->type == POLY_TOTAL_ACCESS) { + /* Remove totally accessible polygon if it contains + ** p + */ + LIST_REMOVE(polygon, entries); + *ret = p; + return PF_OK; + } + + /* Otherwise, compute near point */ + if (near_point(p, polygon, &near_p) == PF_OK) { + *ret = near_p; + + if (!POINT_EQUAL(p, *ret)) + *ret_pol = polygon; + + return PF_OK; + } + + return PF_FATAL; + } + + /* p is not contained in any polygon */ + *ret = p; + return PF_OK; +} + +static vertex_t * +merge_point(pf_state_t *s, point_t v) +/* Merges a point into the polygon set. A new vertex is allocated for this +** point, unless a matching vertex already exists. If the point is on an +** already existing edge that edge is split up into two edges connected by +** the new vertex +** Parameters: (pf_state_t *) s: The pathfinding state +** (point_t) v: The point to merge +** Returns : (vertex_t *) The vertex corresponding to v +*/ +{ + vertex_t *vertex; + vertex_t *v_new; + polygon_t *polygon; + + /* Check for already existing vertex */ + LIST_FOREACH(polygon, &s->polygons, entries) { + CLIST_FOREACH(vertex, &polygon->vertices, entries) + if (POINT_EQUAL(vertex->v, v)) + return vertex; + } + + v_new = vertex_new(v); + + /* Check for point being on an edge */ + LIST_FOREACH(polygon, &s->polygons, entries) + /* Skip single-vertex polygons */ + if (VERTEX_HAS_EDGES(CLIST_FIRST(&polygon->vertices))) + CLIST_FOREACH(vertex, &polygon->vertices, entries) { + vertex_t *next = CLIST_NEXT(vertex, entries); + + if (between(vertex->v, next->v, v)) { + /* Split edge by adding vertex */ + CLIST_INSERT_AFTER(vertex, v_new, entries); + return v_new; + } + } + + /* Add point as single-vertex polygon */ + polygon = polygon_new(POLY_BARRED_ACCESS); + CLIST_INSERT_HEAD(&polygon->vertices, v_new, entries); + LIST_INSERT_HEAD(&s->polygons, polygon, entries); + + return v_new; +} + +static polygon_t * +convert_polygon(state_t *s, reg_t polygon) +/* Converts an SCI polygon into a polygon_t +** Parameters: (state_t *) s: The game state +** (reg_t) polygon: The SCI polygon to convert +** Returns : (polygon_t *) The converted polygon +*/ +{ + int i; + reg_t points = GET_SEL32(polygon, points); + int size = KP_UINT(GET_SEL32(polygon, size)); + unsigned char *list = kernel_dereference_bulk_pointer(s, points, size * POLY_POINT_SIZE); + polygon_t *poly = polygon_new(KP_UINT(GET_SEL32(polygon, type))); + int is_reg_t = polygon_is_reg_t(list, size); + + for (i = 0; i < size; i++) { + vertex_t *vertex = vertex_new(read_point(list, is_reg_t, i)); + CLIST_INSERT_HEAD(&poly->vertices, vertex, entries); + } + + fix_vertex_order(poly); + + return poly; +} + +static void +free_polygon(polygon_t *polygon) +/* Frees a polygon and its vertices +** Parameters: (polygon_t *) polygons: The polygon +** Returns : (void) +*/ +{ + while (!CLIST_EMPTY(&polygon->vertices)) { + vertex_t *vertex = CLIST_FIRST(&polygon->vertices); + CLIST_REMOVE(&polygon->vertices, vertex, entries); + sci_free(vertex); + } + + sci_free(polygon); +} + +static void +free_pf_state(pf_state_t *p) +/* Frees a pathfinding state +** Parameters: (pf_state_t *) p: The pathfinding state +** Returns : (void) +*/ +{ + if (p->vertex_index) + sci_free(p->vertex_index); + + if (p->vis_matrix) + sci_free(p->vis_matrix); + + while (!LIST_EMPTY(&p->polygons)) { + polygon_t *polygon = LIST_FIRST(&p->polygons); + LIST_REMOVE(polygon, entries); + free_polygon(polygon); + } + + sci_free(p); +} + +static void +change_polygons_opt_0(pf_state_t *s) +/* Changes the polygon list for optimization level 0 (used for keyboard +** support). Totally accessible polygons are removed and near-point +** accessible polygons are changed into totally accessible polygons. +** Parameters: (pf_state_t *) s: The pathfinding state +** Returns : (void) +*/ +{ + polygon_t *polygon = LIST_FIRST(&s->polygons); + + while (polygon) { + polygon_t *next = LIST_NEXT(polygon, entries); + + if (polygon->type == POLY_NEAREST_ACCESS) + polygon->type = POLY_TOTAL_ACCESS; + else if (polygon->type == POLY_TOTAL_ACCESS) { + LIST_REMOVE(polygon, entries); + free_polygon(polygon); + } + + polygon = next; + } +} + +static pf_state_t * +convert_polygon_set(state_t *s, reg_t poly_list, point_t start, point_t end, int opt) +/* Converts the SCI input data for pathfinding +** Parameters: (state_t *) s: The game state +** (reg_t) poly_list: Polygon list +** (point_t) start: The start point +** (point_t) end: The end point +** (int) opt: Optimization level (0, 1 or 2) +** Returns : (pf_state_t *) On success a newly allocated pathfinding state, +** NULL otherwise +*/ +{ + polygon_t *polygon; + int err; + int count = 0; + pf_state_t *pf_s = (pf_state_t*)sci_malloc(sizeof(pf_state_t)); + + LIST_INIT(&pf_s->polygons); + pf_s->start = start; + pf_s->end = end; + pf_s->keep_start = 0; + pf_s->keep_end = 0; + pf_s->vertex_index = NULL; + + /* Convert all polygons */ + if (poly_list.segment) { + list_t *list = LOOKUP_LIST(poly_list); + node_t *node = LOOKUP_NODE(list->first); + + while (node) { + polygon = convert_polygon(s, node->value); + LIST_INSERT_HEAD(&pf_s->polygons, polygon, entries); + count += KP_UINT(GET_SEL32(node->value, size)); + node = LOOKUP_NODE(node->succ); + } + } + + if (opt == 0) { + /* Keyboard support */ + change_polygons_opt_0(pf_s); + + /* Find nearest intersection */ + err = nearest_intersection(pf_s, start, end, &start); + + if (err == PF_FATAL) { + sciprintf("[avoidpath] Error: fatal error finding nearest intersecton\n"); + free_pf_state(pf_s); + return NULL; + } + else if (err == PF_OK) + /* Keep original start position if intersection + ** was found + */ + pf_s->keep_start = 1; + } else { + if (fix_point(pf_s, start, &start, &polygon) != PF_OK) { + sciprintf("[avoidpath] Error: couldn't fix start position for pathfinding\n"); + free_pf_state(pf_s); + return NULL; + } + else if (polygon) { + /* Start position has moved */ + pf_s->keep_start = 1; + if ((polygon->type != POLY_NEAREST_ACCESS)) + sciprintf("[avoidpath] Warning: start position at unreachable location\n"); + } + } + + if (fix_point(pf_s, end, &end, &polygon) != PF_OK) { + sciprintf("[avoidpath] Error: couldn't fix end position for pathfinding\n"); + free_pf_state(pf_s); + return NULL; + } + else { + /* Keep original end position if it is contained in a + ** near-point accessible polygon + */ + if (polygon && (polygon->type == POLY_NEAREST_ACCESS)) + pf_s->keep_end = 1; + } + + /* Merge start and end points into polygon set */ + pf_s->vertex_start = merge_point(pf_s, start); + pf_s->vertex_end = merge_point(pf_s, end); + + /* Allocate and build vertex index */ + pf_s->vertex_index = (vertex_t**)sci_malloc(sizeof(vertex_t *) * (count + 2)); + + count = 0; + + LIST_FOREACH(polygon, &pf_s->polygons, entries) { + vertex_t *vertex; + + CLIST_FOREACH(vertex, &polygon->vertices, entries) { + vertex->idx = count; + pf_s->vertex_index[count++] = vertex; + } + } + + pf_s->vertices = count; + + /* Allocate and clear visibility matrix */ + pf_s->vis_matrix = (char*)sci_calloc(pf_s->vertices * VIS_MATRIX_ROW_SIZE(pf_s->vertices), 1); + + return pf_s; +} + +static void +visibility_graph(pf_state_t *s) +/* Computes the visibility graph +** Parameters: (pf_state_t *) s: The pathfinding state +** Returns : (void) +*/ +{ + polygon_t *polygon; + + LIST_FOREACH(polygon, &s->polygons, entries) { + vertex_t *vertex; + + CLIST_FOREACH(vertex, &polygon->vertices, entries) + visible_vertices(s, vertex); + } +} + +static int +intersecting_polygons(pf_state_t *s) +/* Detects (self-)intersecting polygons +** Parameters: (pf_state_t *) s: The pathfinding state +** Returns : (int) 1 if s contains (self-)intersecting polygons, 0 otherwise +*/ +{ + int i, j; + + for (i = 0; i < s->vertices; i++) { + vertex_t *v1 = s->vertex_index[i]; + if (!VERTEX_HAS_EDGES(v1)) + continue; + for (j = i + 1; j < s->vertices; j++) { + vertex_t *v2 = s->vertex_index[j]; + if (!VERTEX_HAS_EDGES(v2)) + continue; + + /* Skip neighbouring edges */ + if ((CLIST_NEXT(v1, entries) == v2) + || CLIST_PREV(v1, entries) == v2) + continue; + + if (intersect(v1->v, CLIST_NEXT(v1, entries)->v, + v2->v, CLIST_NEXT(v2, entries)->v)) + return 1; + } + } + + return 0; +} + +static void +dijkstra(pf_state_t *s) +/* Computes a shortest path from vertex_start to vertex_end. The caller can +** construct the resulting path by following the path_prev links from +** vertex_end back to vertex_start. If no path exists vertex_end->path_prev +** will be NULL +** Parameters: (pf_state_t *) s: The pathfinding state +** Returns : (void) +*/ +{ + polygon_t *polygon; + /* Vertices of which the shortest path is known */ + LIST_HEAD(done_head, vertex) done; + /* The remaining vertices */ + LIST_HEAD(remain_head, vertex) remain; + + LIST_INIT(&remain); + LIST_INIT(&done); + + /* Start out with all vertices in set remain */ + LIST_FOREACH(polygon, &s->polygons, entries) { + vertex_t *vertex; + + CLIST_FOREACH(vertex, &polygon->vertices, entries) + LIST_INSERT_HEAD(&remain, vertex, dijkstra); + } + + s->vertex_start->dist = 0.0f; + + /* Loop until we find vertex_end */ + while (1) { + int i; + vertex_t *vertex, *vertex_min; + float min = HUGE_DISTANCE; + + /* Find vertex at shortest distance from set done */ + LIST_FOREACH(vertex, &remain, dijkstra) { + if (vertex->dist < min) { + vertex_min = vertex; + min = vertex->dist; + } + } + + if (min == HUGE_DISTANCE) { + sciprintf("[avoidpath] Warning: end point (%i, %i) is unreachable\n", s->vertex_end->v.x, s->vertex_end->v.y); + return; + } + + /* If vertex_end is at shortest distance we can stop */ + if (vertex_min == s->vertex_end) + return; + + /* Move vertex from set remain to set done */ + LIST_REMOVE(vertex_min, dijkstra); + LIST_INSERT_HEAD(&done, vertex_min, dijkstra); + + for (i = 0; i < s->vertices; i++) { + /* Adjust upper bound for all vertices that are visible from vertex_min */ + if (IS_VISIBLE(s, vertex_min->idx, i)) { + float new_dist; + + /* Avoid plotting path along screen edge */ + if ((s->vertex_index[i] != s->vertex_end) && point_on_screen_border(s->vertex_index[i]->v)) + continue; + + new_dist = vertex_min->dist + distance(to_pointf(vertex_min->v), + to_pointf(s->vertex_index[i]->v)); + if (new_dist < s->vertex_index[i]->dist) { + s->vertex_index[i]->dist = new_dist; + s->vertex_index[i]->path_prev = vertex_min; + } + } + } + } +} + +static reg_t +output_path(pf_state_t *p, state_t *s) +/* Stores the final path in newly allocated dynmem +** Parameters: (pf_state_t *) p: The pathfinding state +** (state_t *) s: The game state +** Returns : (reg_t) Pointer to dynmem containing path +*/ +{ + int path_len = 0; + byte *oref; + reg_t output; + vertex_t *vertex = p->vertex_end; + int i; + int unreachable = vertex->path_prev == NULL; + + if (unreachable) { + /* If pathfinding failed we only return the path up to vertex_start */ + oref = sm_alloc_dynmem(&s->seg_manager, POLY_POINT_SIZE * 3, + AVOIDPATH_DYNMEM_STRING, &output); + + if (p->keep_start) + POLY_SET_POINT(oref, 0, p->start.x, p->start.y); + else + POLY_SET_POINT(oref, 0, p->vertex_start->v.x, p->vertex_start->v.y); + POLY_SET_POINT(oref, 1, p->vertex_start->v.x, p->vertex_start->v.y); + POLY_SET_POINT(oref, 2, POLY_LAST_POINT, POLY_LAST_POINT); + + return output; + } + + while (vertex) { + /* Compute path length */ + path_len++; + vertex = vertex->path_prev; + } + + oref = sm_alloc_dynmem(&s->seg_manager, POLY_POINT_SIZE * (path_len + 1 + p->keep_start + p->keep_end), + AVOIDPATH_DYNMEM_STRING, &output); + + /* Sentinel */ + POLY_SET_POINT(oref, path_len + p->keep_start + p->keep_end, POLY_LAST_POINT, POLY_LAST_POINT); + + /* Add original start and end points if needed */ + if (p->keep_end) + POLY_SET_POINT(oref, path_len + p->keep_start, p->end.x, p->end.y); + if (p->keep_start) + POLY_SET_POINT(oref, 0, p->start.x, p->start.y); + + i = path_len + p->keep_start - 1; + + if (unreachable) { + /* Return straight trajectory from start to end */ + POLY_SET_POINT(oref, i - 1, p->vertex_start->v.x, p->vertex_start->v.y); + POLY_SET_POINT(oref, i, p->vertex_end->v.x, p->vertex_end->v.y); + return output; + } + + vertex = p->vertex_end; + while (vertex) { + POLY_SET_POINT(oref, i, vertex->v.x, vertex->v.y); + vertex = vertex->path_prev; + i--; + } + + if (s->debug_mode & (1 << SCIkAVOIDPATH_NR)) { + sciprintf("[avoidpath] Returning path:"); + for (i = 0; i < path_len + p->keep_start + p->keep_end; i++) { + point_t pt; + POLY_GET_POINT(oref, i, pt.x, pt.y); + sciprintf(" (%i, %i)", pt.x, pt.y); + } + sciprintf("\n"); + } + + return output; +} + +reg_t +kAvoidPath(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + point_t start = gfx_point(SKPV(0), SKPV(1)); + + if (s->debug_mode & (1 << SCIkAVOIDPATH_NR)) { + gfxw_port_t *port= s->picture_port; + + if (!port->decorations) { + port->decorations = gfxw_new_list(gfx_rect(0, 0, 320, 200), 0); + port->decorations->set_visual(GFXW(port->decorations), port->visual); + } else { + port->decorations->free_contents(port->decorations); + } + } + + switch (argc) { + + case 3 : + { + reg_t retval; + polygon_t *polygon = convert_polygon(s, argv[2]); + + if (polygon->type == POLY_CONTAINED_ACCESS) { + sciprintf("[avoidpath] Warning: containment test performed on contained access polygon\n"); + + /* Semantics unknown, assume barred access semantics */ + polygon->type = POLY_BARRED_ACCESS; + } + + retval = make_reg(0, contained(start, polygon) != CONT_OUTSIDE); + free_polygon(polygon); + return retval; + } + case 6 : + case 7 : + { + point_t end = gfx_point(SKPV(2), SKPV(3)); + reg_t poly_list = argv[4]; + /* int poly_list_size = UKPV(5); */ + int opt = UKPV_OR_ALT(6, 1); + reg_t output; + pf_state_t *p; + + if (s->debug_mode & (1 << SCIkAVOIDPATH_NR)) { + sciprintf("[avoidpath] Pathfinding input:\n"); + draw_point(s, start, 1); + draw_point(s, end, 0); + + if (poly_list.segment) { + print_input(s, poly_list, start, end, opt); + draw_input(s, poly_list, start, end, opt); + } + } + + p = convert_polygon_set(s, poly_list, start, end, opt); + + if (intersecting_polygons(p)) { + sciprintf("[avoidpath] Error: input set contains (self-)intersecting polygons\n"); + free_pf_state(p); + p = NULL; + } + + if (!p) { + byte *oref; + sciprintf("[avoidpath] Error: pathfinding failed for following input:\n"); + print_input(s, poly_list, start, end, opt); + sciprintf("[avoidpath] Returning direct path from start point to end point\n"); + oref = sm_alloc_dynmem(&s->seg_manager, POLY_POINT_SIZE*3, + AVOIDPATH_DYNMEM_STRING, &output); + + POLY_SET_POINT(oref, 0, start.x, start.y); + POLY_SET_POINT(oref, 1, end.x, end.y); + POLY_SET_POINT(oref, 2, POLY_LAST_POINT, POLY_LAST_POINT); + + return output; + } + + visibility_graph(p); + dijkstra(p); + + output = output_path(p, s); + free_pf_state(p); + + /* Memory is freed by explicit calls to Memory */ + return output; + } + + default: + SCIkwarn(SCIkWARNING, "Unknown AvoidPath subfunction %d\n", + argc); + return NULL_REG; + break; + } +} diff --git a/engines/sci/engine/kscripts.c b/engines/sci/engine/kscripts.c new file mode 100644 index 0000000000..1fd7fd9a85 --- /dev/null +++ b/engines/sci/engine/kscripts.c @@ -0,0 +1,353 @@ +/*************************************************************************** + kscripts.c.c Copyright (C) 1999 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + +#include +#include +#include "kernel_types.h" + +reg_t +read_selector(state_t *s, reg_t object, selector_t selector_id, const char *file, int line) +{ + reg_t *address; + + if (lookup_selector(s, object, selector_id, &address, NULL) != SELECTOR_VARIABLE) + return NULL_REG; + else + return *address; +} + + +void +write_selector(state_t *s, reg_t object, selector_t selector_id, reg_t value, + const char *fname, int line) +{ + reg_t *address; + + if ((selector_id < 0) || (selector_id > s->selector_names_nr)) { + SCIkwarn(SCIkWARNING, "Attempt to write to invalid selector %d of" + " object at "PREG" (%s L%d).\n", selector_id, + PRINT_REG(object), fname, line); + return; + } + + if (lookup_selector(s, object, selector_id, &address, NULL) != SELECTOR_VARIABLE) + SCIkwarn(SCIkWARNING, "Selector '%s' of object at %04x could not be" + " written to (%s L%d)\n", + s->selector_names[selector_id], object, fname, line); + else + *address = value; + +} + +int +invoke_selector(state_t *s, reg_t object, int selector_id, int noinvalid, int kfunct, + stack_ptr_t k_argp, int k_argc, /* Kernel function argp/argc */ + const char *fname, int line, int argc, ...) +{ + va_list argp; + int i; + int framesize = 2 + 1 * argc; + reg_t address; + int slc_type; + stack_ptr_t stackframe = k_argp + k_argc; + + exec_stack_t *xstack; /* Execution stack */ + + stackframe[0] = make_reg(0, selector_id); /* The selector we want to call */ + stackframe[1] = make_reg(0, argc); /* Argument count */ + + slc_type = lookup_selector(s, object, selector_id, NULL, &address); + + if (slc_type == SELECTOR_NONE) { + SCIkwarn(SCIkERROR, "Selector '%s' of object at "PREG" could not be invoked (%s L%d)\n", + s->selector_names[selector_id], PRINT_REG(object), fname, line); + if (noinvalid == 0) + KERNEL_OOPS("Not recoverable: VM was halted\n"); + return 1; + } + if (slc_type == SELECTOR_VARIABLE) /* Swallow silently */ + return 0; + + va_start(argp, argc); + for (i = 0; i < argc; i++) { + reg_t arg = va_arg(argp, reg_t); + stackframe[2 + i] = arg; /* Write each argument */ + } + va_end(argp); + + /* Write "kernel" call to the stack, for debugging: */ + xstack = add_exec_stack_entry(s, NULL_REG, NULL, NULL_REG, + k_argc, k_argp - 1, 0, NULL_REG, + s->execution_stack_pos, SCI_XS_CALLEE_LOCALS); + xstack->selector = -42 - kfunct; /* Evil debugging hack to identify kernel function */ + xstack->type = EXEC_STACK_TYPE_KERNEL; + + /* Now commit the actual function: */ + xstack = send_selector(s, object, object, + stackframe, framesize, stackframe); + + xstack->sp += argc+2; + xstack->fp += argc+2; + + run_vm(s, 0); /* Start a new vm */ + + --(s->execution_stack_pos); /* Get rid of the extra stack entry */ + + return 0; +} + + +int +is_object(state_t *s, reg_t object) +{ + return obj_get(s, object) != NULL; +} + + +/* kLoad(restype, resnrs ... ): +** Loads arbitrary resources of type 'restype' with resource numbers 'resnrs' +** This implementation ignores all resource numbers except the first one. +*/ +reg_t +kLoad(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int restype = KP_UINT(argv[0]); + int resnr = KP_UINT(argv[1]); + + if (restype == sci_memory)/* Request to dynamically allocate hunk memory for later use */ + return kalloc(s, "kLoad()", resnr); + + return make_reg(0, ((restype << 11) | resnr)); /* Return the resource identifier as handle */ +} + +reg_t +kLock(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int restype = UKPV(0)&0x7f; + int resnr = UKPV(1); + int state = argc > 2 ? UKPV(2) : 1; + + resource_t *which; + + switch (state) + { + case 1 : + scir_find_resource(s->resmgr, restype, resnr, 1); + break; + case 0 : + which = scir_find_resource(s->resmgr, restype, resnr, 0); + scir_unlock_resource(s->resmgr, which, resnr, restype); + break; + } + return s->r_acc; +} + +/* kUnload(): +** Unloads an arbitrary resource of type 'restype' with resource numbber 'resnr' +*/ +reg_t +kUnLoad(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int restype = KP_UINT(argv[0]); + reg_t resnr = argv[1]; + + if (restype == sci_memory) + kfree(s, resnr); + + return s->r_acc; +} + + +reg_t +kClone(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t parent_addr = argv[0]; + object_t *parent_obj = obj_get(s, parent_addr); + reg_t clone_addr; + clone_t *clone_obj; /* same as object_t* */ + int varblock_size; + + + if (!parent_obj) { + SCIkwarn(SCIkERROR, "Attempt to clone non-object/class at "PREG" failed", PRINT_REG(parent_addr)); + return NULL_REG; + } + + SCIkdebug(SCIkMEM, "Attempting to clone from "PREG"\n", PRINT_REG(parent_addr)); + + clone_obj = sm_alloc_clone(&s->seg_manager, &clone_addr); + + if (!clone_obj) { + SCIkwarn(SCIkERROR, "Cloning "PREG" failed-- internal error!\n", PRINT_REG(parent_addr)); + return NULL_REG; + } + + memcpy(clone_obj, parent_obj, sizeof(clone_t)); + clone_obj->flags = 0; + varblock_size = parent_obj->variables_nr * sizeof(reg_t); + clone_obj->variables = (reg_t*)sci_malloc(varblock_size); + memcpy(clone_obj->variables, parent_obj->variables, varblock_size); + + /* Mark as clone */ + clone_obj->variables[SCRIPT_INFO_SELECTOR].offset = SCRIPT_INFO_CLONE; + clone_obj->variables[SCRIPT_SPECIES_SELECTOR] = clone_obj->pos; + if (IS_CLASS(parent_obj)) + clone_obj->variables[SCRIPT_SUPERCLASS_SELECTOR] = parent_obj->pos; + sm_increment_lockers(&s->seg_manager, parent_obj->pos.segment, SEG_ID); + sm_increment_lockers(&s->seg_manager, clone_obj->pos.segment, SEG_ID); + + return clone_addr; +} + + +extern void +_k_view_list_mark_free(state_t *s, reg_t off); /* kgraphics.c */ + +reg_t +kDisposeClone(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t victim_addr = argv[0]; + clone_t *victim_obj = obj_get(s, victim_addr); + word underBits; + + if (!victim_obj) { + SCIkwarn(SCIkERROR, "Attempt to dispose non-class/object at "PREG"\n", + PRINT_REG(victim_addr)); + return s->r_acc; + } + + if (victim_obj->variables[SCRIPT_INFO_SELECTOR].offset != SCRIPT_INFO_CLONE) { + /* SCIkwarn("Attempt to dispose something other than a clone at %04x\n", offset); */ + /* SCI silently ignores this behaviour; some games actually depend on it */ + return s->r_acc; + } + + underBits = GET_SEL32V(victim_addr, underBits); + if (underBits) { + SCIkwarn(SCIkWARNING,"Clone "PREG" was cleared with underBits set\n", PRINT_REG(victim_addr)); + } +#if 0 + if (s->dyn_views) { /* Free any widget associated with the clone */ + gfxw_widget_t *widget = gfxw_set_id(gfxw_remove_ID(s->dyn_views, offset), GFXW_NO_ID); + + if (widget && s->bg_widgets) + s->bg_widgets->add(GFXWC(s->bg_widgets), widget); + } +#endif + + victim_obj->flags |= OBJECT_FLAG_FREED; + + _k_view_list_mark_free(s, victim_addr); /* Free on view list, if neccessary */ + + return s->r_acc; +} + + +/* kScriptID(script, index): +** Returns script dispatch address index in the supplied script +*/ +reg_t +kScriptID(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int script = KP_UINT(argv[0]); + int index = KP_UINT(KP_ALT(1, NULL_REG)); + + seg_id_t scriptid = script_get_segment(s, script, SCRIPT_GET_LOAD); + script_t *scr; + + if (argv[0].segment) + return argv[0]; + + if (!scriptid) + return NULL_REG; + + scr = &(s->seg_manager.heap[scriptid]->data.script); + + if (!scr->exports_nr) { + SCIkdebug(SCIkERROR, "Script 0x%x does not have a dispatch table\n", script); + return NULL_REG; + } + + if (index > scr->exports_nr) { + SCIkwarn(SCIkERROR, "Dispatch index too big: %d > %d\n", + index, scr->exports_nr); + return NULL_REG; + } + + return make_reg(scriptid, sm_validate_export_func(&s->seg_manager, index, scriptid)); +} + + +reg_t +kDisposeScript(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int script = argv[0].offset; + + /* Work around QfG1 graveyard bug */ + if (argv[0].segment) return s->r_acc; + + if (sm_script_is_loaded(&(s->seg_manager), script, SCRIPT_ID)) + { + int id = sm_seg_get(&(s->seg_manager), script); + + if (s->execution_stack[s->execution_stack_pos].addr.pc.segment != id) + sm_set_lockers(&(s->seg_manager), 1, script, SCRIPT_ID); + } + + script_uninstantiate(s, script); + s->execution_stack_pos_changed = 1; + return s->r_acc; +} + +int +is_heap_object(state_t *s, reg_t pos) +{ + object_t *obj = obj_get(s, pos); + return (obj != NULL + && (!(obj->flags & OBJECT_FLAG_FREED)) + && (!sm_script_is_marked_as_deleted(&s->seg_manager, pos.segment))); +} + +reg_t +kIsObject(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + if (argv[0].offset == 0xffff) /* Treated specially */ + return NULL_REG; + else + return make_reg(0, is_heap_object(s, argv[0])); +} + +reg_t +kRespondsTo(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t obj = argv[0]; + int selector = KP_UINT(argv[1]); + + return make_reg(0, is_heap_object(s, obj) + && lookup_selector(s, obj, selector, NULL, NULL) != SELECTOR_NONE); +} + diff --git a/engines/sci/engine/ksound.c b/engines/sci/engine/ksound.c new file mode 100644 index 0000000000..e95f219e4b --- /dev/null +++ b/engines/sci/engine/ksound.c @@ -0,0 +1,948 @@ +/*************************************************************************** + ksound.c Copyright (C) 1999 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + +#include +#include + +#define _K_SCI0_SOUND_INIT_HANDLE 0 +#define _K_SCI0_SOUND_PLAY_HANDLE 1 +#define _K_SCI0_SOUND_NOP 2 +#define _K_SCI0_SOUND_DISPOSE_HANDLE 3 +#define _K_SCI0_SOUND_MUTE_SOUND 4 +#define _K_SCI0_SOUND_STOP_HANDLE 5 +#define _K_SCI0_SOUND_SUSPEND_HANDLE 6 +#define _K_SCI0_SOUND_RESUME_HANDLE 7 +#define _K_SCI0_SOUND_VOLUME 8 +#define _K_SCI0_SOUND_UPDATE_VOL_PRI 9 +#define _K_SCI0_SOUND_FADE_HANDLE 10 +#define _K_SCI0_SOUND_GET_POLYPHONY 11 +#define _K_SCI0_SOUND_PLAY_NEXT 12 + +#define _K_SCI01_SOUND_MASTER_VOLME 0 /* Set/Get */ +#define _K_SCI01_SOUND_MUTE_SOUND 1 +#define _K_SCI01_SOUND_UNUSED 2 +#define _K_SCI01_SOUND_GET_POLYPHONY 3 +#define _K_SCI01_SOUND_UPDATE_HANDLE 4 +#define _K_SCI01_SOUND_INIT_HANDLE 5 +#define _K_SCI01_SOUND_DISPOSE_HANDLE 6 +#define _K_SCI01_SOUND_PLAY_HANDLE 7 +#define _K_SCI01_SOUND_STOP_HANDLE 8 +#define _K_SCI01_SOUND_SUSPEND_HANDLE 9 /* or resume */ +#define _K_SCI01_SOUND_FADE_HANDLE 10 +#define _K_SCI01_SOUND_UPDATE_CUES 11 +#define _K_SCI01_SOUND_MIDI_SEND 12 +#define _K_SCI01_SOUND_REVERB 13 /* Get/Set */ +#define _K_SCI01_SOUND_HOLD 14 + +#define _K_SCI1_SOUND_MASTER_VOLME 0 /* Set/Get */ +#define _K_SCI1_SOUND_MUTE_SOUND 1 +#define _K_SCI1_SOUND_UNUSED1 2 +#define _K_SCI1_SOUND_GET_POLYPHONY 3 +#define _K_SCI1_SOUND_GET_AUDIO_CAPABILITY 4 +#define _K_SCI1_SOUND_SUSPEND_SOUND 5 +#define _K_SCI1_SOUND_INIT_HANDLE 6 +#define _K_SCI1_SOUND_DISPOSE_HANDLE 7 +#define _K_SCI1_SOUND_PLAY_HANDLE 8 +#define _K_SCI1_SOUND_STOP_HANDLE 9 +#define _K_SCI1_SOUND_SUSPEND_HANDLE 10 /* or resume */ +#define _K_SCI1_SOUND_FADE_HANDLE 11 +#define _K_SCI1_SOUND_HOLD_HANDLE 12 +#define _K_SCI1_SOUND_UNUSED2 13 +#define _K_SCI1_SOUND_SET_HANDLE_VOLUME 14 +#define _K_SCI1_SOUND_SET_HANDLE_PRIORITY 15 +#define _K_SCI1_SOUND_SET_HANDLE_LOOP 16 +#define _K_SCI1_SOUND_UPDATE_CUES 17 +#define _K_SCI1_SOUND_MIDI_SEND 18 +#define _K_SCI1_SOUND_REVERB 19 /* Get/Set */ +#define _K_SCI1_SOUND_UPDATE_VOL_PRI 20 + +#define _K_SCI1_AUDIO_POSITION 6 /* Return current position in audio stream */ + +#define SCI1_SOUND_FLAG_MAY_PAUSE 1 /* Only here for completeness; The interpreter doesn't touch this bit */ +#define SCI1_SOUND_FLAG_SCRIPTED_PRI 2 /* but does touch this */ + +#define FROBNICATE_HANDLE(reg) ((reg).segment << 16 | (reg).offset) +#define DEFROBNICATE_HANDLE(handle) (make_reg((handle >> 16) & 0xffff, handle & 0xffff)) +#define SCRIPT_ASSERT_ZERO(fun) if (fun) script_debug_flag = script_error_flag = 1; + + +static void +script_set_priority(state_t *s, reg_t obj, int priority) +{ + int song_nr = GET_SEL32V(obj, number); + resource_t *song = scir_find_resource(s->resmgr, sci_sound, song_nr, 0); + int flags = GET_SEL32V(obj, flags); + + if (priority == -1) + { + if (song->data[0] == 0xf0) + priority = song->data[1]; else + SCIkdebug(SCIkWARNING, "Attempt to unset song priority when there is no built-in value!\n"); + + flags &= ~SCI1_SOUND_FLAG_SCRIPTED_PRI; + } else flags |= SCI1_SOUND_FLAG_SCRIPTED_PRI; + + sfx_song_renice(&s->sound, FROBNICATE_HANDLE(obj), priority); + PUT_SEL32V(obj, flags, flags); +} + +song_iterator_t * +build_iterator(state_t *s, int song_nr, int type, songit_id_t id) +{ + resource_t *song = scir_find_resource(s->resmgr, sci_sound, song_nr, 0); + + if (!song) + return NULL; + + return songit_new(song->data, song->size, type, id); +} + + +void +process_sound_events(state_t *s) /* Get all sound events, apply their changes to the heap */ +{ + int result; + song_handle_t handle; + int cue; + + if (s->version>=SCI_VERSION_FTU_DOSOUND_VARIANT_1) + return; + /* SCI01 and later explicitly poll for everything */ + + while ((result = sfx_poll(&s->sound, &handle, &cue))) { + reg_t obj = DEFROBNICATE_HANDLE(handle); + if (!is_object(s, obj)) { + SCIkdebug(SCIkWARNING, "Non-object "PREG" received sound signal (%d/%d)\n", PRINT_REG(obj), result, cue); + return; + } + + switch (result) { + + case SI_LOOP: + SCIkdebug(SCIkSOUND, "[process-sound] Song "PREG" looped (to %d)\n", + PRINT_REG(obj), cue); + /* PUT_SEL32V(obj, loops, GET_SEL32V(obj, loop) - 1);*/ + PUT_SEL32V(obj, signal, -1); + break; + + case SI_RELATIVE_CUE: + SCIkdebug(SCIkSOUND, "[process-sound] Song "PREG" received relative cue %d\n", + PRINT_REG(obj), cue); + PUT_SEL32V(obj, signal, cue + 0x7f); + break; + + case SI_ABSOLUTE_CUE: + SCIkdebug(SCIkSOUND, "[process-sound] Song "PREG" received absolute cue %d\n", + PRINT_REG(obj), cue); + PUT_SEL32V(obj, signal, cue); + break; + + case SI_FINISHED: + SCIkdebug(SCIkSOUND, "[process-sound] Song "PREG" finished\n", + PRINT_REG(obj)); + PUT_SEL32V(obj, signal, -1); + PUT_SEL32V(obj, state, _K_SOUND_STATUS_STOPPED); + break; + + default: + sciprintf("Unexpected result from sfx_poll: %d\n", result); + break; + } + } +} + + +reg_t +kDoSound_SCI0(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t obj = KP_ALT(1, NULL_REG); + word command = UKPV(0); + song_handle_t handle = FROBNICATE_HANDLE(obj); + int number = obj.segment ? + GET_SEL32V(obj, number) : + -1; /* We were not going to use it anyway */ + + if (s->debug_mode & (1 << SCIkSOUNDCHK_NR)) { + int i; + + SCIkdebug(SCIkSOUND, "Command 0x%x", command); + switch (command) { + case 0: sciprintf("[InitObj]"); break; + case 1: sciprintf("[Play]"); break; + case 2: sciprintf("[NOP]"); break; + case 3: sciprintf("[DisposeHandle]"); break; + case 4: sciprintf("[SetSoundOn(?)]"); break; + case 5: sciprintf("[Stop]"); break; + case 6: sciprintf("[Suspend]"); break; + case 7: sciprintf("[Resume]"); break; + case 8: sciprintf("[Get(Set?)Volume]"); break; + case 9: sciprintf("[Signal: Obj changed]"); break; + case 10: sciprintf("[Fade(?)]"); break; + case 11: sciprintf("[ChkDriver]"); break; + case 12: sciprintf("[PlayNextSong (formerly StopAll)]"); break; + default: sciprintf("[unknown]"); break; + } + + sciprintf("("); + for (i = 1; i < argc; i++) { + sciprintf(PREG, PRINT_REG(argv[i])); + if (i + 1 < argc) + sciprintf(", "); + } + sciprintf(")\n"); + } + + + switch (command) { + case _K_SCI0_SOUND_INIT_HANDLE: + if (obj.segment) { + sciprintf("Initializing song number %d\n", GET_SEL32V(obj, number)); + SCRIPT_ASSERT_ZERO(sfx_add_song(&s->sound, + build_iterator(s, number, + SCI_SONG_ITERATOR_TYPE_SCI0, + handle), + 0, handle, number)); + PUT_SEL32V(obj, state, _K_SOUND_STATUS_INITIALIZED); + PUT_SEL32(obj, handle, obj); /* ``sound handle'': we use the object address */ + } + break; + + case _K_SCI0_SOUND_PLAY_HANDLE: + if (obj.segment) { + sfx_song_set_status(&s->sound, + handle, SOUND_STATUS_PLAYING); + sfx_song_set_loops(&s->sound, + handle, GET_SEL32V(obj, loop)); + PUT_SEL32V(obj, state, _K_SOUND_STATUS_PLAYING); + } + break; + + case _K_SCI0_SOUND_NOP: + break; + + case _K_SCI0_SOUND_DISPOSE_HANDLE: + if (obj.segment) { + sfx_remove_song(&s->sound, handle); + } + PUT_SEL32V(obj, handle, 0x0000); + break; + + case _K_SCI0_SOUND_STOP_HANDLE: + if (obj.segment) { + sfx_song_set_status(&s->sound, + handle, SOUND_STATUS_STOPPED); + PUT_SEL32V(obj, state, SOUND_STATUS_STOPPED); + } + break; + + case _K_SCI0_SOUND_SUSPEND_HANDLE: + if (obj.segment) { + sfx_song_set_status(&s->sound, + handle, SOUND_STATUS_SUSPENDED); + PUT_SEL32V(obj, state, SOUND_STATUS_SUSPENDED); + } + break; + + case _K_SCI0_SOUND_RESUME_HANDLE: + if (obj.segment) { + sfx_song_set_status(&s->sound, + handle, SOUND_STATUS_PLAYING); + PUT_SEL32V(obj, state, SOUND_STATUS_PLAYING); + } + break; + + case _K_SCI0_SOUND_MUTE_SOUND: { + /* if there's a parameter, we're setting it. Otherwise, + we're querying it. */ + /*int param = UPARAM_OR_ALT(1,-1); + + if (param != -1) + s->acc = s->sound_server->command(s, SOUND_COMMAND_SET_MUTE, 0, param); + else + s->acc = s->sound_server->command(s, SOUND_COMMAND_GET_MUTE, 0, 0);*/ + + } + break; + + case _K_SCI0_SOUND_VOLUME: { + /* range from 0x0 to 0xf */ + /* parameter optional. If present, set.*/ + int vol = SKPV_OR_ALT(1, -1); + + if (vol != -1) + sfx_set_volume(&s->sound, vol << 0xf); + else + s->r_acc = make_reg(0, sfx_get_volume(&s->sound) >> 0xf); + } + break; + + case _K_SCI0_SOUND_UPDATE_VOL_PRI: + if (obj.segment) { + sfx_song_set_loops(&s->sound, + handle, GET_SEL32V(obj, loop)); + script_set_priority(s, obj, GET_SEL32V(obj, pri)); + } + break; + + case _K_SCI0_SOUND_FADE_HANDLE: + /*s->sound_server->command(s, SOUND_COMMAND_FADE_HANDLE, obj, 120);*/ /* Fade out in 2 secs */ + /* FIXME: The next couple of lines actually STOP the handle, rather + ** than fading it! */ + if (obj.segment) { + sfx_song_set_status(&s->sound, + handle, SOUND_STATUS_STOPPED); + PUT_SEL32V(obj, state, SOUND_STATUS_STOPPED); + PUT_SEL32V(obj, signal, -1); + } + break; + + case _K_SCI0_SOUND_GET_POLYPHONY: + s->r_acc = make_reg(0, sfx_get_player_polyphony()); + break; + + case _K_SCI0_SOUND_PLAY_NEXT: + /* sfx_all_stop(&s->sound);*/ + break; + + default: + SCIkwarn(SCIkWARNING, "Unhandled DoSound command: %x\n", command); + + } + // process_sound_events(s); /* Take care of incoming events */ + + return s->r_acc; +} + +int +sfx_send_midi(sfx_state_t *self, song_handle_t handle, int channel, + int command, int arg1, int arg2); + +reg_t +kDoSound_SCI01(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + word command = UKPV(0); + reg_t obj = KP_ALT(1, NULL_REG); + song_handle_t handle = FROBNICATE_HANDLE(obj); + int number = obj.segment ? + GET_SEL32V(obj, number) : + -1; /* We were not going to use it anyway */ + + if ((s->debug_mode & (1 << SCIkSOUNDCHK_NR)) + && command != _K_SCI01_SOUND_UPDATE_CUES) { + int i; + + SCIkdebug(SCIkSOUND, "Command 0x%x", command); + switch (command) { + case 0: sciprintf("[MasterVolume]"); break; + case 1: sciprintf("[Mute]"); break; + case 2: sciprintf("[NOP(2)]"); break; + case 3: sciprintf("[GetPolyphony]"); break; + case 4: sciprintf("[Update]"); break; + case 5: sciprintf("[Init]"); break; + case 6: sciprintf("[Dispose]"); break; + case 7: sciprintf("[Play]"); break; + case 8: sciprintf("[Stop]"); break; + case 9: sciprintf("[Suspend]"); break; + case 10: sciprintf("[Fade]"); break; + case 11: sciprintf("[UpdateCues]"); break; + case 12: sciprintf("[MidiSend]"); break; + case 13: sciprintf("[Reverb]"); break; + case 14: sciprintf("[Hold]"); break; + default: sciprintf("[unknown]"); break; + } + + sciprintf("("); + for (i = 1; i < argc; i++) { + sciprintf(PREG, PRINT_REG(argv[i])); + if (i + 1 < argc) + sciprintf(", "); + } + sciprintf(")\n"); + } + + switch (command) + { + case _K_SCI01_SOUND_MASTER_VOLME : + { + int vol = SKPV_OR_ALT(1, -1); + + if (vol != -1) + sfx_set_volume(&s->sound, vol << 0xf); + else + s->r_acc = make_reg(0, sfx_get_volume(&s->sound) >> 0xf); + break; + } + case _K_SCI01_SOUND_MUTE_SOUND : + { + /* if there's a parameter, we're setting it. Otherwise, + we're querying it. */ + /*int param = UPARAM_OR_ALT(1,-1); + + if (param != -1) + s->acc = s->sound_server->command(s, SOUND_COMMAND_SET_MUTE, 0, param); + else + s->acc = s->sound_server->command(s, SOUND_COMMAND_GET_MUTE, 0, 0);*/ + + break; + } + case _K_SCI01_SOUND_UNUSED : + { + break; + } + case _K_SCI01_SOUND_GET_POLYPHONY : + { + s->r_acc = make_reg(0, sfx_get_player_polyphony()); + break; + } + case _K_SCI01_SOUND_PLAY_HANDLE : + { + int looping = GET_SEL32V(obj, loop); + int vol = GET_SEL32V(obj, vol); + int pri = GET_SEL32V(obj, pri); + RESTORE_BEHAVIOR rb = (RESTORE_BEHAVIOR) UKPV(2); /* Too lazy to look up a default value for this */ + + if (obj.segment) { + sfx_song_set_status(&s->sound, + handle, SOUND_STATUS_PLAYING); + sfx_song_set_loops(&s->sound, + handle, looping); + sfx_song_renice(&s->sound, + handle, pri); + song_lib_set_restore_behavior(s->sound.songlib, handle, rb); + } + + break; + } + case _K_SCI01_SOUND_INIT_HANDLE : + { + int looping = GET_SEL32V(obj, loop); + int vol = GET_SEL32V(obj, vol); + int pri = GET_SEL32V(obj, pri); + + if (obj.segment && (scir_test_resource(s->resmgr, sci_sound, number))) + { + sciprintf("Initializing song number %d\n", number); + SCRIPT_ASSERT_ZERO(sfx_add_song(&s->sound, + build_iterator(s, number, + SCI_SONG_ITERATOR_TYPE_SCI1, + handle), + 0, handle, number)); + PUT_SEL32(obj, nodePtr, obj); + PUT_SEL32(obj, handle, obj); + } + break; + } + case _K_SCI01_SOUND_DISPOSE_HANDLE : + { + if (obj.segment) { + sfx_song_set_status(&s->sound, + handle, SOUND_STATUS_STOPPED); + sfx_remove_song(&s->sound, handle); + } + break; + } + case _K_SCI01_SOUND_UPDATE_HANDLE : + { + /* FIXME: Get these from the sound server */ + int signal = 0; + int min = 0; + int sec = 0; + int frame = 0; + + /* FIXME: Update the sound server state with 'vol' */ + int looping = GET_SEL32V(obj, loop); + int vol = GET_SEL32V(obj, vol); + int pri = GET_SEL32V(obj, pri); + + sfx_song_set_loops(&s->sound, + handle, looping); + sfx_song_renice(&s->sound, handle, pri); + + SCIkdebug(SCIkSOUND, "[sound01-update-handle] -- CUE "PREG); + + PUT_SEL32V(obj, signal, signal); + PUT_SEL32V(obj, min, min); + PUT_SEL32V(obj, sec, sec); + PUT_SEL32V(obj, frame, frame); + + break; + } + case _K_SCI01_SOUND_STOP_HANDLE : + { + PUT_SEL32V(obj, signal, -1); + if (obj.segment) { + sfx_song_set_status(&s->sound, + handle, SOUND_STATUS_STOPPED); + } + break; + } + case _K_SCI01_SOUND_SUSPEND_HANDLE : + { + int state = UKPV(2); + int setstate = (state)? + SOUND_STATUS_SUSPENDED : SOUND_STATUS_PLAYING; + + if (obj.segment) { + sfx_song_set_status(&s->sound, + handle, setstate); + } + break; + } + case _K_SCI01_SOUND_FADE_HANDLE : + { + /* There are four parameters that control the fade here. + * TODO: Figure out the exact semantics */ + + /* FIXME: The next couple of lines actually STOP the song right away */ + PUT_SEL32V(obj, signal, -1); + if (obj.segment) { + sfx_song_set_status(&s->sound, + handle, SOUND_STATUS_STOPPED); + } + break; + } + case _K_SCI01_SOUND_UPDATE_CUES : + { + int signal = 0; + int min = 0; + int sec = 0; + int frame = 0; + int result = SI_LOOP; /* small hack */ + int cue; + + while (result == SI_LOOP) + result = sfx_poll_specific(&s->sound, handle, &cue); + + switch (result) { + + case SI_ABSOLUTE_CUE: + signal = cue; + SCIkdebug(SCIkSOUND, "--- [CUE] "PREG" Absolute Cue: %d\n", + PRINT_REG(obj), signal); + + PUT_SEL32V(obj, signal, signal); + break; + + case SI_RELATIVE_CUE: + signal = cue; + SCIkdebug(SCIkSOUND, "--- [CUE] "PREG" Relative Cue: %d\n", + PRINT_REG(obj), cue); + + /* FIXME to match commented-out semantics + * below, with proper storage of dataInc and + * signal in the iterator code. */ + PUT_SEL32V(obj, dataInc, signal); + PUT_SEL32V(obj, signal, signal); + break; + + case SI_FINISHED: + SCIkdebug(SCIkSOUND, "--- [FINISHED] "PREG"\n", PRINT_REG(obj)); + PUT_SEL32V(obj, signal, 0xffff); + break; + + case SI_LOOP: + break; /* Doesn't happen */ + } + +/* switch (signal) */ +/* { */ +/* case 0x00: */ +/* if (dataInc!=GET_SEL32V(obj, dataInc)) */ +/* { */ +/* PUT_SEL32V(obj, dataInc, dataInc); */ +/* PUT_SEL32V(obj, signal, dataInc+0x7f); */ +/* } else */ +/* { */ +/* PUT_SEL32V(obj, signal, signal); */ +/* } */ +/* break; */ +/* case 0xFF: /\* May be unnecessary *\/ */ +/* sfx_song_set_status(&s->sound, */ +/* handle, SOUND_STATUS_STOPPED); */ +/* break; */ +/* default : */ +/* if (dataInc!=GET_SEL32V(obj, dataInc)) */ +/* { */ +/* PUT_SEL32V(obj, dataInc, dataInc); */ +/* PUT_SEL32V(obj, signal, dataInc+0x7f); */ +/* } else */ +/* { */ +/* PUT_SEL32V(obj, signal, signal); */ +/* } */ +/* break; */ +/* } */ + + PUT_SEL32V(obj, min, min); + PUT_SEL32V(obj, sec, sec); + PUT_SEL32V(obj, frame, frame); + break; + } + case _K_SCI01_SOUND_MIDI_SEND : + { + int channel = SKPV(2); + int command = UKPV(3) == 0xff ? + 0xe0 : /* Pitch wheel */ + 0xb0; /* UKPV(3) is actually a controller number */ + int controller = UKPV(3); + int param = UKPV(4); + + sfx_send_midi(&s->sound, handle, + channel, command, controller, param); + break; + } + case _K_SCI01_SOUND_REVERB : + { + break; + } + case _K_SCI01_SOUND_HOLD : + { + int flag = SKPV(2); + break; + } + } + + return s->r_acc; +} + +int +sfx_send_midi(sfx_state_t *self, song_handle_t handle, int channel, + int command, int arg1, int arg2); + +reg_t +kDoSound_SCI1(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + word command = UKPV(0); + reg_t obj = KP_ALT(1, NULL_REG); + song_handle_t handle = FROBNICATE_HANDLE(obj); + int number = obj.segment ? + GET_SEL32V(obj, number) : + -1; /* We were not going to use it anyway */ + + CHECK_THIS_KERNEL_FUNCTION; + + if ((s->debug_mode & (1 << SCIkSOUNDCHK_NR)) + && command != _K_SCI1_SOUND_UPDATE_CUES) { + int i; + + SCIkdebug(SCIkSOUND, "Command 0x%x", command); + switch (command) { + case 0: sciprintf("[MasterVolume]"); break; + case 1: sciprintf("[Mute]"); break; + case 2: sciprintf("[NOP(2)]"); break; + case 3: sciprintf("[GetPolyphony]"); break; + case 4: sciprintf("[GetAudioCapability]"); break; + case 5: sciprintf("[GlobalSuspend]"); break; + case 6: sciprintf("[Init]"); break; + case 7: sciprintf("[Dispose]"); break; + case 8: sciprintf("[Play]"); break; + case 9: sciprintf("[Stop]"); break; + case 10: sciprintf("[SuspendHandle]"); break; + case 11: sciprintf("[Fade]"); break; + case 12: sciprintf("[Hold]"); break; + case 13: sciprintf("[Unused(13)]"); break; + case 14: sciprintf("[SetVolume]"); break; + case 15: sciprintf("[SetPriority]"); break; + case 16: sciprintf("[SetLoop]"); break; + case 17: sciprintf("[UpdateCues]"); break; + case 18: sciprintf("[MidiSend]"); break; + case 19: sciprintf("[Reverb]"); break; + case 20: sciprintf("[UpdateVolPri]"); break; + default: sciprintf("[unknown]"); break; + } + + sciprintf("("); + for (i = 1; i < argc; i++) { + sciprintf(PREG, PRINT_REG(argv[i])); + if (i + 1 < argc) + sciprintf(", "); + } + sciprintf(")\n"); + } + + switch (command) + { + case _K_SCI1_SOUND_MASTER_VOLME : + { + /*int vol = UPARAM_OR_ALT (1, -1); + + if (vol != -1) + s->acc = s->sound_server->command(s, SOUND_COMMAND_SET_VOLUME, 0, vol); + else + s->acc = s->sound_server->command(s, SOUND_COMMAND_GET_VOLUME, 0, 0); + break;*/ + } + case _K_SCI1_SOUND_MUTE_SOUND : + { + /* if there's a parameter, we're setting it. Otherwise, + we're querying it. */ + /*int param = UPARAM_OR_ALT(1,-1); + + if (param != -1) + s->acc = s->sound_server->command(s, SOUND_COMMAND_SET_MUTE, 0, param); + else + s->acc = s->sound_server->command(s, SOUND_COMMAND_GET_MUTE, 0, 0); + break;*/ + } + case _K_SCI1_SOUND_UNUSED1 : + { + break; + } + case _K_SCI1_SOUND_GET_POLYPHONY : + { + /*s->acc = s->sound_server->command(s, SOUND_COMMAND_TEST, 0, 0);*/ + break; + } + case _K_SCI1_SOUND_GET_AUDIO_CAPABILITY : + { + return NULL_REG; + } + case _K_SCI1_SOUND_PLAY_HANDLE : + { + int looping = GET_SEL32V(obj, loop); + int vol = GET_SEL32V(obj, vol); + int pri = GET_SEL32V(obj, pri); + song_t *song = song_lib_find(s->sound.songlib, handle); + + if (GET_SEL32V(obj, nodePtr) && (song && number != song->resource_num)) + { + sfx_song_set_status(&s->sound, + handle, SOUND_STATUS_STOPPED); + sfx_remove_song(&s->sound, handle); + PUT_SEL32(obj, nodePtr, NULL_REG); + } + + if (!GET_SEL32V(obj, nodePtr) && obj.segment) + { + if (!scir_test_resource(s->resmgr, sci_sound, number)) + { + sciprintf("Could not open song number %d\n", number); + return NULL_REG; + } + + sciprintf("Initializing song number %d\n", number); + SCRIPT_ASSERT_ZERO(sfx_add_song(&s->sound, + build_iterator(s, number, + SCI_SONG_ITERATOR_TYPE_SCI1, + handle), + 0, handle, number)); + PUT_SEL32(obj, nodePtr, obj); + PUT_SEL32(obj, handle, obj); + } + + if (obj.segment) { + sfx_song_set_status(&s->sound, + handle, SOUND_STATUS_PLAYING); + sfx_song_set_loops(&s->sound, + handle, looping); + sfx_song_renice(&s->sound, + handle, pri); + } + + break; + } + case _K_SCI1_SOUND_INIT_HANDLE : + { + int looping = GET_SEL32V(obj, loop); + int vol = GET_SEL32V(obj, vol); + int pri = GET_SEL32V(obj, pri); + + if (GET_SEL32V(obj, nodePtr)) + { + sfx_song_set_status(&s->sound, + handle, SOUND_STATUS_STOPPED); + sfx_remove_song(&s->sound, handle); + } + + if (obj.segment && (scir_test_resource(s->resmgr, sci_sound, number))) { + sciprintf("Initializing song number %d\n", number); + SCRIPT_ASSERT_ZERO(sfx_add_song(&s->sound, + build_iterator(s, number, + SCI_SONG_ITERATOR_TYPE_SCI1, + handle), + 0, handle, number)); + PUT_SEL32(obj, nodePtr, obj); + PUT_SEL32(obj, handle, obj); + } + break; + } + case _K_SCI1_SOUND_DISPOSE_HANDLE : + { + if (obj.segment) { + sfx_song_set_status(&s->sound, + handle, SOUND_STATUS_STOPPED); + sfx_remove_song(&s->sound, handle); + } + break; + } + case _K_SCI1_SOUND_STOP_HANDLE : + { + PUT_SEL32V(obj, signal, -1); + if (obj.segment) { + sfx_song_set_status(&s->sound, + handle, SOUND_STATUS_STOPPED); + } + break; + } + case _K_SCI1_SOUND_SUSPEND_HANDLE : + { + break; + } + case _K_SCI1_SOUND_FADE_HANDLE : + { + fade_params_t fade; + if (obj.segment) { + fade.final_volume = UKPV(2); + fade.ticks_per_step = UKPV(3); + fade.step_size = UKPV(4); + fade.action = UKPV(5) ? + FADE_ACTION_FADE_AND_STOP : + FADE_ACTION_FADE_AND_CONT; + + sfx_song_set_fade(&s->sound, + handle, + &fade); + + /* FIXME: The next couple of lines actually STOP the handle, rather + ** than fading it! */ + if (UKPV(5)) + { + PUT_SEL32V(obj, signal, -1); + PUT_SEL32V(obj, nodePtr, 0); + PUT_SEL32V(obj, handle, 0); + sfx_song_set_status(&s->sound, + handle, SOUND_STATUS_STOPPED); + } + } + break; + } + case _K_SCI1_SOUND_HOLD_HANDLE : + { + int value = SKPV(2); + + sfx_song_set_hold(&s->sound, + handle, value); + break; + } + case _K_SCI1_SOUND_UNUSED2 : + { + break; + } + case _K_SCI1_SOUND_SET_HANDLE_VOLUME : + { + break; + } + case _K_SCI1_SOUND_SET_HANDLE_PRIORITY : + { + int value = SKPV(2); + + script_set_priority(s, obj, value); + break; + } + case _K_SCI1_SOUND_SET_HANDLE_LOOP : + { + break; + } + case _K_SCI1_SOUND_UPDATE_CUES : + { + int signal = 0; + int min = 0; + int sec = 0; + int frame = 0; + int result = SI_LOOP; /* small hack */ + int cue; + + while (result == SI_LOOP) + result = sfx_poll_specific(&s->sound, handle, &cue); + + switch (result) { + + case SI_ABSOLUTE_CUE: + signal = cue; + fprintf(stderr, "[CUE] "PREG" Absolute Cue: %d\n", + PRINT_REG(obj), signal); + + PUT_SEL32V(obj, signal, signal); + break; + + case SI_RELATIVE_CUE: + fprintf(stderr, "[CUE] "PREG" Relative Cue: %d\n", + PRINT_REG(obj), cue); + + PUT_SEL32V(obj, dataInc, cue); + PUT_SEL32V(obj, signal, cue + 127); + break; + + case SI_FINISHED: + PUT_SEL32V(obj, signal, 0xffff); + break; + + case SI_LOOP: + break; /* Doesn't happen */ + } + break; + } + case _K_SCI1_SOUND_MIDI_SEND : + { + sfx_send_midi(&s->sound, handle, + UKPV(2), UKPV(3), UKPV(4), UKPV(5)); + break; + } + case _K_SCI1_SOUND_REVERB : + { + break; + } + case _K_SCI1_SOUND_UPDATE_VOL_PRI : + { + break; + } + } + return s->r_acc; +} + +reg_t +kDoSound(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + if (s->version>=SCI_VERSION_FTU_DOSOUND_VARIANT_2) + return kDoSound_SCI1(s, funct_nr, argc, argv); + else if (s->version>=SCI_VERSION_FTU_DOSOUND_VARIANT_1) + return kDoSound_SCI01(s, funct_nr, argc, argv); + else + return kDoSound_SCI0(s, funct_nr, argc, argv); +} + +reg_t +kDoAudio(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + switch (UKPV(0)) + { + case _K_SCI1_AUDIO_POSITION : + return make_reg(0, -1); /* Finish immediately */ + } + + return s->r_acc; +} + diff --git a/engines/sci/engine/kstring.c b/engines/sci/engine/kstring.c new file mode 100644 index 0000000000..c4b5426329 --- /dev/null +++ b/engines/sci/engine/kstring.c @@ -0,0 +1,802 @@ +/*************************************************************************** + kstring.c Copyright (C) 1999 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ +/* String and parser handling */ + +#include +#include +#include "message.h" + +#define CHECK_OVERFLOW1(pt, size, rv) \ + if (((pt) - (str_base)) + (size) > maxsize) { \ + SCIkwarn(SCIkERROR, "String expansion exceeded heap boundaries\n"); \ + return rv;\ + } + +char * +kernel_lookup_text(state_t *s, reg_t address, int index) + /* Returns the string the script intended to address */ +{ + char *seeker; + resource_t *textres; + + if (address.segment) + return (char *) kernel_dereference_bulk_pointer(s, address, 0); + else { + int textlen; + int _index = index; + textres = scir_find_resource(s->resmgr, sci_text, address.offset, 0); + + if (!textres) { + SCIkwarn(SCIkERROR, "text.%03d not found\n", address); + return NULL; /* Will probably segfault */ + } + + textlen = textres->size; + seeker = (char *) textres->data; + + while (index--) + while ((textlen--) && (*seeker++)); + + if (textlen) + return seeker; + else { + SCIkwarn(SCIkERROR, "Index %d out of bounds in text.%03d\n", _index, address); + return 0; + } + + } +} + + +/*************************************************************/ +/* Parser */ +/**********/ + +#ifdef SCI_SIMPLE_SAID_CODE +int +vocab_match_simple(state_t *s, heap_ptr addr) +{ + int nextitem; + int listpos = 0; + + if (!s->parser_valid) + return SAID_NO_MATCH; + + if (s->parser_valid == 2) { /* debug mode: sim_said */ + do { + sciprintf("DEBUGMATCH: "); + nextitem = s->heap[addr++]; + + if (nextitem < 0xf0) { + nextitem = nextitem << 8 | s->heap[addr++]; + if (s->parser_nodes[listpos].type + || nextitem != s->parser_nodes[listpos++].content.value) + return SAID_NO_MATCH; + } else { + + if (nextitem == 0xff) + return (s->parser_nodes[listpos++].type == -1)? SAID_FULL_MATCH : SAID_NO_MATCH; /* Finished? */ + + if (s->parser_nodes[listpos].type != 1 + || nextitem != s->parser_nodes[listpos++].content.value) + return SAID_NO_MATCH; + + } + } while (42); + } else { /* normal simple match mode */ + return vocab_simple_said_test(s, addr); + } +} +#endif /* SCI_SIMPLE_SAID_CODE */ + + +reg_t +kSaid(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t heap_said_block = argv[0]; + byte *said_block; + int new_lastmatch; + + if (!heap_said_block.segment) + return NULL_REG; + + said_block = (byte *) kernel_dereference_bulk_pointer(s, heap_said_block, 0); + + if (!said_block) { + SCIkdebug(SCIkWARNING, "Said on non-string, pointer "PREG"\n", PRINT_REG(heap_said_block)); + return NULL_REG; + } + + if (s->debug_mode & (1 << SCIkPARSER_NR)) { + SCIkdebug(SCIkPARSER, "Said block:", 0); + vocab_decypher_said_block(s, said_block); + } + + if (IS_NULL_REG(s->parser_event) || (GET_SEL32V(s->parser_event, claimed))) { + return NULL_REG; + } + +#ifdef SCI_SIMPLE_SAID_CODE + + s->acc = 0; + + if (s->parser_lastmatch_word == SAID_FULL_MATCH) + return; /* Matched before; we're not doing any more matching work today. */ + + if ((new_lastmatch = vocab_match_simple(s, said_block)) != SAID_NO_MATCH) { + + if (s->debug_mode & (1 << SCIkPARSER_NR)) + sciprintf("Match (simple).\n"); + s->acc = 1; + + if (new_lastmatch == SAID_FULL_MATCH) /* Finished matching? */ + PUT_SELECTOR(s->parser_event, claimed, 1); /* claim event */ + /* otherwise, we have a partial match: Set new lastmatch word in all cases. */ + + s->parser_lastmatch_word = new_lastmatch; + } + +#else /* !SCI_SIMPLE_SAID_CODE */ + if ((new_lastmatch = said(s, said_block, (s->debug_mode & (1 << SCIkPARSER_NR)))) + != SAID_NO_MATCH) { /* Build and possibly display a parse tree */ + + if (s->debug_mode & (1 << SCIkPARSER_NR)) + sciprintf("Match.\n"); + + s->r_acc = make_reg(0, 1); + + if (new_lastmatch != SAID_PARTIAL_MATCH) + PUT_SEL32V(s->parser_event, claimed, 1); + + s->parser_lastmatch_word = new_lastmatch; + + } else { + s->parser_lastmatch_word = SAID_NO_MATCH; + return NULL_REG; + } +#endif /* !SCI_SIMPLE_SAID_CODE */ + return s->r_acc; +} + + +reg_t +kSetSynonyms(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t object = argv[0]; + list_t *list; + node_t *node; + int script; + int synpos = 0; + + if (s->synonyms_nr) + free(s->synonyms); + + s->synonyms_nr = 0; + + list = LOOKUP_LIST(GET_SEL32(object, elements)); + node = LOOKUP_NODE(list->first); + + while (node) { + reg_t objpos = node->value; + int seg; + int synonyms_nr; + + script = GET_SEL32V(objpos, number); + seg = sm_seg_get(&(s->seg_manager), script); + + if (seg >= 0) synonyms_nr = sm_get_synonyms_nr(&(s->seg_manager), seg, SEG_ID); + + if (synonyms_nr) { + byte *synonyms; + + synonyms = sm_get_synonyms(&(s->seg_manager), seg, SEG_ID); + if (synonyms) { + int i; + if (s->synonyms_nr) + s->synonyms = (synonym_t*)sci_realloc(s->synonyms, + sizeof(synonym_t) * (s->synonyms_nr + synonyms_nr)); + else + s->synonyms = (synonym_t*)sci_malloc(sizeof(synonym_t) * synonyms_nr); + + s->synonyms_nr += synonyms_nr; + + SCIkdebug(SCIkPARSER, "Setting %d synonyms for script.%d\n", + synonyms_nr, script); + + if (synonyms_nr > 16384) { + SCIkwarn(SCIkERROR, "Segtable corruption: script.%03d has %d synonyms!\n", + script, synonyms_nr); + /* We used to reset the corrupted value here. I really don't think it's appropriate. + * Lars */ + } else + for (i = 0; i < synonyms_nr; i++) { + s->synonyms[synpos].replaceant = getInt16(synonyms + i * 4); + s->synonyms[synpos].replacement = getInt16(synonyms + i * 4 + 2); + + synpos++; + } + } else SCIkwarn(SCIkWARNING, "Synonyms of script.%03d were requested, but script is not available\n"); + + } + + node = LOOKUP_NODE(node->succ); + } + + SCIkdebug(SCIkPARSER, "A total of %d synonyms are active now.\n", s->synonyms_nr); + + if (!s->synonyms_nr) + s->synonyms = NULL; + return s->r_acc; +} + + + +reg_t +kParse(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t stringpos = argv[0]; + char *string = kernel_dereference_char_pointer(s, stringpos, 0); + int words_nr; + char *error; + result_word_t *words; + reg_t event = argv[1]; + + s->parser_event = event; + + s->parser_lastmatch_word = SAID_NO_MATCH; + + if (s->parser_valid == 2) { + sciprintf("Parsing skipped: Parser in simparse mode\n"); + return s->r_acc; + } + + words = vocab_tokenize_string(string, &words_nr, + s->parser_words, s->parser_words_nr, + s->parser_suffices, s->parser_suffices_nr, + &error); + s->parser_valid = 0; /* not valid */ + + if (words) { + + int syntax_fail = 0; + + vocab_synonymize_tokens(words, words_nr, s->synonyms, s->synonyms_nr); + + s->r_acc = make_reg(0, 1); + + if (s->debug_mode & (1 << SCIkPARSER_NR)) { + int i; + + SCIkdebug(SCIkPARSER, "Parsed to the following blocks:\n", 0); + + for (i = 0; i < words_nr; i++) + SCIkdebug(SCIkPARSER, " Type[%04x] Group[%04x]\n", words[i].w_class, words[i].group); + } + + if (vocab_build_parse_tree(&(s->parser_nodes[0]), words, words_nr, s->parser_branches, + s->parser_rules)) + syntax_fail = 1; /* Building a tree failed */ + +#ifdef SCI_SIMPLE_SAID_CODE + vocab_build_simple_parse_tree(&(s->parser_nodes[0]), words, words_nr); +#endif /* SCI_SIMPLE_SAID_CODE */ + + free(words); + + if (syntax_fail) { + + s->r_acc = make_reg(0, 1); + PUT_SEL32V(event, claimed, 1); + + invoke_selector(INV_SEL(s->game_obj, syntaxFail, 0), 2, s->parser_base, stringpos); + /* Issue warning */ + + SCIkdebug(SCIkPARSER, "Tree building failed\n"); + + } else { + s->parser_valid = 1; + PUT_SEL32V(event, claimed, 0); +#ifndef SCI_SIMPLE_SAID_CODE + if (s->debug_mode & (1 << SCIkPARSER_NR)) + vocab_dump_parse_tree("Parse-tree", s->parser_nodes); +#endif /* !SCI_SIMPLE_SAID_CODE */ + } + + } else { + + s->r_acc = make_reg(0, 0); + PUT_SEL32V(event, claimed, 1); + if (error) { + char *pbase_str = kernel_dereference_char_pointer(s, s->parser_base, 0); + strcpy(pbase_str, error); + SCIkdebug(SCIkPARSER,"Word unknown: %s\n", error); + /* Issue warning: */ + + invoke_selector(INV_SEL(s->game_obj, wordFail, 0), 2, s->parser_base, stringpos); + free(error); + return make_reg(0, 1); /* Tell them that it dind't work */ + } + } + + return s->r_acc; +} + + +reg_t +kStrEnd(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + reg_t address = argv[0]; + char *seeker = kernel_dereference_char_pointer(s, address, 0); + + while (*seeker++) + ++address.offset; + + return address; +} + +reg_t +kStrCat(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char *s1 = kernel_dereference_char_pointer(s, argv[0], 0); + char *s2 = kernel_dereference_char_pointer(s, argv[1], 0); + + strcat(s1, s2); + return argv[0]; +} + +reg_t +kStrCmp(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char *s1 = kernel_dereference_char_pointer(s, argv[0], 0); + char *s2 = kernel_dereference_char_pointer(s, argv[1], 0); + + if (argc > 2) + return make_reg(0, strncmp(s1, s2, UKPV(2))); + else + return make_reg(0, strcmp(s1, s2)); +} + + +reg_t +kStrCpy(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char *dest = (char *) kernel_dereference_bulk_pointer(s, argv[0], 0); + char *src = (char *) kernel_dereference_bulk_pointer(s, argv[1], 0); + + if (!dest) { + SCIkdebug(SCIkWARNING, "Attempt to strcpy TO invalid pointer "PREG"!\n", + PRINT_REG(argv[0])); + return NULL_REG; + } + if (!src) { + SCIkdebug(SCIkWARNING, "Attempt to strcpy FROM invalid pointer "PREG"!\n", + PRINT_REG(argv[1])); + return NULL_REG; + } + + if (argc > 2) + { + int length = SKPV(2); + + if (length>=0) + strncpy(dest, src, length); + else { + if (s->seg_manager.heap[argv[0].segment]->type == MEM_OBJ_DYNMEM) { + reg_t *srcp = (reg_t *) src; + + int i; + SCIkdebug(SCIkWARNING, "Performing reg_t to raw conversion for AvoidPath\n"); + for (i = 0; i < -length / 2; i++) { + dest[2 * i] = srcp->offset & 0xff; + dest[2 * i + 1] = srcp->offset >> 8; + srcp++; + } + } else + memcpy(dest, src, -length); + } + } + else + strcpy(dest, src); + + return argv[0]; +} + + +reg_t +kStrAt(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + unsigned char *dest = (unsigned char *) kernel_dereference_bulk_pointer(s, argv[0], 0); + reg_t *dest2; + + if (!dest) { + SCIkdebug(SCIkWARNING, "Attempt to StrAt at invalid pointer "PREG"!\n", + PRINT_REG(argv[0])); + return NULL_REG; + } + + if ((argc == 2) && +/* Our pathfinder already works around the issue we're trying to fix */ + (strcmp(sm_get_description(&(s->seg_manager), argv[0]), + AVOIDPATH_DYNMEM_STRING) != 0) && + ((strlen((const char*)dest) < 2) || (!is_print_str((char*)dest)))) + /* SQ4 array handling detected */ + { +#ifndef WORDS_BIGENDIAN + int odd = KP_UINT(argv[1]) & 1; +#else + int odd = !(KP_UINT(argv[1]) & 1); +#endif + dest2 = ((reg_t *) dest)+(KP_UINT(argv[1])/2); + dest = ((unsigned char *) (&dest2->offset))+odd; + } else dest += KP_UINT(argv[1]); + + s->r_acc = make_reg(0, *dest); + + if (argc > 2) + *dest = KP_SINT(argv[2]); /* Request to modify this char */ + + return s->r_acc; +} + + +reg_t +kReadNumber(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char *source = kernel_dereference_char_pointer(s, argv[0], 0); + + while (isspace(*source)) + source++; /* Skip whitespace */ + + if (*source == '$') /* SCI uses this for hex numbers */ + return make_reg(0, (gint16)strtol(source + 1, NULL, 16)); /* Hex */ + else + return make_reg(0, (gint16)strtol(source, NULL, 10)); /* Force decimal */ +} + + +#define ALIGN_NONE 0 +#define ALIGN_RIGHT 1 +#define ALIGN_LEFT -1 +#define ALIGN_CENTRE 2 + +/* Format(targ_address, textresnr, index_inside_res, ...) +** or +** Format(targ_address, heap_text_addr, ...) +** Formats the text from text.textresnr (offset index_inside_res) or heap_text_addr according to +** the supplied parameters and writes it to the targ_address. +*/ +reg_t +kFormat(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + int *arguments; + reg_t dest = argv[0]; + char *target = (char *) kernel_dereference_bulk_pointer(s, dest, 0); + reg_t position = argv[1]; /* source */ + int index = UKPV(2); + char *source; + char *str_base = target; + int mode = 0; + int paramindex = 0; /* Next parameter to evaluate */ + char xfer; + int i; + int startarg; + int str_leng = 0; /* Used for stuff like "%13s" */ + int unsigned_var = 0; + int maxsize = 4096; /* Arbitrary... */ + + + if (position.segment) + startarg = 2; + else + startarg = 3; /* First parameter to use for formatting */ + + source = kernel_lookup_text(s, position, index); + + SCIkdebug(SCIkSTRINGS, "Formatting \"%s\"\n", source); + + + arguments = (int*)sci_malloc(sizeof(int) * argc); +#ifdef SATISFY_PURIFY + memset(arguments, 0, sizeof(int) * argc); +#endif + + for (i = startarg; i < argc; i++) + arguments[i-startarg] = UKPV(i); /* Parameters are copied to prevent overwriting */ + + while ((xfer = *source++)) { + if (xfer == '%') { + if (mode == 1) { + CHECK_OVERFLOW1(target, 2, NULL_REG); + *target++ = '%'; /* Literal % by using "%%" */ + mode = 0; + } else { + mode = 1; + str_leng = 0; + } + } else if (mode == 1) { /* xfer != '%' */ + char fillchar = ' '; + int align = ALIGN_NONE; + + char *writestart = target; /* Start of the written string, used after the switch */ + + /* int writelength; -- unused atm */ + + if (xfer && (isdigit(xfer) || xfer == '-' || xfer == '=')) { + char *destp; + + if (xfer == '0') + fillchar = '0'; else + + if (xfer == '=') { + align = ALIGN_CENTRE; + source++; + } else + + if (isdigit(xfer)) + source--; /* Stepped over length argument */ + + str_leng = strtol(source, &destp, 10); + + if (destp > source) + source = destp; + + if (str_leng < 0) { + align = ALIGN_LEFT; + str_leng = -str_leng; + } else if (align != ALIGN_CENTRE) + align = ALIGN_RIGHT; + + xfer = *source++; + } else + str_leng = 0; + + CHECK_OVERFLOW1(target, str_leng + 1, NULL_REG); + + switch (xfer) { + case 's': { /* Copy string */ + reg_t reg = argv[startarg + paramindex]; + char *tempsource = kernel_lookup_text(s, reg, + arguments[paramindex + 1]); + int slen = strlen(tempsource); + int extralen = str_leng - slen; + CHECK_OVERFLOW1(target, extralen, NULL_REG); + if (extralen < 0) + extralen = 0; + + if (reg.segment) /* Heap address? */ + paramindex++; + else + paramindex += 2; /* No, text resource address */ + + switch (align) { + + case ALIGN_NONE: + case ALIGN_RIGHT: + while (extralen-- > 0) + *target++ = ' '; /* Format into the text */ + break; + + case ALIGN_CENTRE: { + int half_extralen = extralen >> 1; + while (half_extralen-- > 0) + *target++ = ' '; /* Format into the text */ + break;} + + default: break; + + } + + strcpy(target, tempsource); + target += slen; + + switch (align) { + + case ALIGN_CENTRE: { + int half_extralen; + align = 0; + half_extralen = extralen - (extralen >> 1); + while (half_extralen-- > 0) + *target++ = ' '; /* Format into the text */ + break;} + + default: break; + + } + + mode = 0; + } + break; + + case 'c': { /* insert character */ + CHECK_OVERFLOW1(target, 2, NULL_REG); + if (align >= 0) + while (str_leng-- > 1) + *target++ = ' '; /* Format into the text */ + + *target++ = arguments[paramindex++]; + mode = 0; + } + break; + + case 'x': + case 'u': unsigned_var = 1; + case 'd': { /* Copy decimal */ + /* int templen; -- unused atm */ + const char *format_string = "%d"; + + if (xfer == 'x') + format_string = "%x"; + + if (!unsigned_var) + if (arguments[paramindex] & 0x8000) + /* sign extend */ + arguments[paramindex] = (~0xffff) | arguments[paramindex]; + + target += sprintf(target, format_string, arguments[paramindex++]); + CHECK_OVERFLOW1(target, 0, NULL_REG); + + unsigned_var = 0; + + mode = 0; + } + break; + default: + *target = '%'; + target++; + *target = xfer; + target++; + mode = 0; + } + + if (align) { + int written = target - writestart; + int padding = str_leng - written; + + if (padding > 0) { + if (align > 0) { + memmove(writestart + padding, + writestart, written); + memset(writestart, fillchar, padding); + } else { + memset(target, ' ', padding); + } + target += padding; + } + } + }else { /* mode != 1 */ + *target = xfer; + target++; + } + } + + free(arguments); + + *target = 0; /* Terminate string */ + return dest; /* Return target addr */ +} + + +reg_t +kStrLen(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + char *str = kernel_dereference_char_pointer(s, argv[0], 0); + + return make_reg(0, strlen(str)); +} + + +reg_t +kGetFarText(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + resource_t *textres = scir_find_resource(s->resmgr, sci_text, UKPV(0), 0); + char *seeker; + int counter = UKPV(1); + + + if (!textres) { + SCIkwarn(SCIkERROR, "text.%d does not exist\n", UKPV(0)); + return NULL_REG; + } + + seeker = (char *) textres->data; + + while (counter--) + while (*seeker++); + /* The second parameter (counter) determines the number of the string inside the text + ** resource. + */ + + strcpy(kernel_dereference_char_pointer(s, argv[2], 0), seeker); /* Copy the string and get return value */ + return argv[2]; +} + +#define DUMMY_MESSAGE "No MESSAGE support in FreeSCI yet" + +static message_state_t state; + +reg_t +kMessage(state_t *s, int funct_nr, int argc, reg_t *argv) +{ + if (!state.initialized) + message_state_initialize(s->resmgr, &state); + + switch (UKPV(0)) + { + case 0 : + { + char *buffer = argc == 7 ? kernel_dereference_char_pointer(s, argv[6], 0) : NULL; + message_tuple_t tuple; + int module = UKPV(1); + + tuple.noun = UKPV(2); + tuple.verb = UKPV(3); + tuple.cond = UKPV(4); + tuple.seq = UKPV(5); + + if (message_state_load_res(&state, module) && message_get_specific(&state, &tuple)) + { + if (buffer) + message_get_text(&state, buffer, 100); + return make_reg(0, message_get_talker(&state)); /* Talker id */ + } else + { + if (buffer) strcpy(buffer, DUMMY_MESSAGE); + return NULL_REG; + } + } + case 1 : + { + char *buffer = argc == 7 ? kernel_dereference_char_pointer(s, argv[6], 0) : NULL; + + if (message_get_next(&state)) + { + if (buffer) + message_get_text(&state, buffer, 100); + return make_reg(0, message_get_talker(&state)); /* Talker id */ + } else + { + if (buffer) strcpy(buffer, DUMMY_MESSAGE); + return NULL_REG; + } + } + case 2 : + { + message_tuple_t tuple; + int module = UKPV(1); + tuple.noun = UKPV(2); + tuple.verb = UKPV(3); + tuple.cond = UKPV(4); + tuple.seq = UKPV(5); + + if (message_state_load_res(&state, module) && message_get_specific(&state, &tuple)) + return make_reg(0, message_get_length(&state)+1); + else return NULL_REG; + } + } + + return NULL_REG; +} diff --git a/engines/sci/engine/makefile.dos b/engines/sci/engine/makefile.dos new file mode 100644 index 0000000000..0d4c5fac8e --- /dev/null +++ b/engines/sci/engine/makefile.dos @@ -0,0 +1,22 @@ +# +# FreeSCI/DOS Makefile +# +# 19991220 rink created this file +# 20000615 rink updated this file +# +# +TARGET : libsciengine.a + +FILES = savegame.o kernel.o kscripts.o klists.o kfile.o kgraphics.o \ + kmath.o kevent.o kstring.o kmenu.o ksound.o vm.o game.o \ + scriptdebug.o heap.o simplesaid.o said.o grammar.o + + +CC = gcc +CFLAGS = -g -c -I../include -I../.. -D_DOS -DHAVE_LIBPNG -DHAVE_UNISTD_H + +clean: + del *.o *.a + +libsciengine.a: ${FILES} + ar r libsciengine.a ${FILES} diff --git a/engines/sci/engine/message.c b/engines/sci/engine/message.c new file mode 100644 index 0000000000..97171a9763 --- /dev/null +++ b/engines/sci/engine/message.c @@ -0,0 +1,226 @@ +/*************************************************************************** + message.c Copyright (C) 2008 Lars Skovlund + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Lars Skovlund (LS) [lskovlun@image.dk] + +***************************************************************************/ + +#include "message.h" + +static +int get_talker_trivial(index_record_cursor_t *cursor) +{ + return -1; +} + +/* Version 2.101 and later code ahead */ + +static +void index_record_parse_2101(index_record_cursor_t *cursor, message_tuple_t *t) +{ + int noun = *(cursor->index_record + 0); + int verb = *(cursor->index_record + 1); + + t->noun = noun; + t->verb = verb; + t->cond = t->seq = 0; +} + +static +void index_record_get_text_2101(index_record_cursor_t *cursor, char *buffer, int buffer_size) +{ + int offset = getUInt16(cursor->index_record + 2); + char *stringptr = (char *)cursor->resource_beginning + offset; + + strncpy(buffer, stringptr, buffer_size); +} + +static +int header_get_index_record_count_2101(byte *header) +{ + return getUInt16(header + 4); +} + +/* Version 3.411 and later code ahead */ + +static +void index_record_parse_3411(index_record_cursor_t *cursor, message_tuple_t *t) +{ + int noun = *(cursor->index_record + 0); + int verb = *(cursor->index_record + 1); + int cond = *(cursor->index_record + 2); + int seq = *(cursor->index_record + 3); + + t->noun = noun; + t->verb = verb; + t->cond = cond; + t->seq = seq; +} + +static +int index_record_get_talker_3411(index_record_cursor_t *cursor) +{ + return *(cursor->index_record + 4); +} + +static +void index_record_get_text_3411(index_record_cursor_t *cursor, char *buffer, int buffer_size) +{ + int offset = getUInt16(cursor->index_record + 5); + char *stringptr = (char *)cursor->resource_beginning + offset; + + strncpy(buffer, stringptr, buffer_size); +} + +static +int header_get_index_record_count_3411(byte *header) +{ + return getUInt16(header + 8); +} + +/* Generic code from here on */ + +static +int four_tuple_match(message_tuple_t *t1, message_tuple_t *t2) +{ + return + t1->noun == t2->noun && + t1->verb == t2->verb && + t1->cond == t2->cond && + t1->seq == t2->seq; +} + +static +void index_record_cursor_initialize(message_state_t *state, index_record_cursor_t *cursor) +{ + cursor->resource_beginning = state->current_res->data; + cursor->index_record = state->index_records; + cursor->index = 1; +} + +static +int index_record_next(message_state_t *state, index_record_cursor_t *cursor) +{ + if (cursor->index == state->record_count) + return 0; + cursor->index_record += state->handler->index_record_size; + cursor->index ++; + return 1; +} + +static +int index_record_find(message_state_t *state, message_tuple_t *t, index_record_cursor_t *cursor) +{ + message_tuple_t looking_at; + int found = 0; + + index_record_cursor_initialize(state, cursor); + + do + { + state->handler->parse(cursor, &looking_at); + if (four_tuple_match(t, &looking_at)) + found = 1; + } while (!found && index_record_next(state, cursor)); + + // FIXME: Recursion not handled yet + + return found; +} + +int message_get_specific(message_state_t *state, message_tuple_t *t) +{ + return index_record_find(state, t, &state->engine_cursor); +} + +int message_get_next(message_state_t *state) +{ + return index_record_next(state, &state->engine_cursor); +} + +int message_get_talker(message_state_t *state) +{ + return state->handler->get_talker(&state->engine_cursor); +} + +int message_get_text(message_state_t *state, char *buffer, int length) +{ + state->handler->get_text(&state->engine_cursor, buffer, length); + return strlen(buffer); +} + +int message_get_length(message_state_t *state) +{ + char buffer[500]; + + state->handler->get_text(&state->engine_cursor, buffer, sizeof(buffer)); + return strlen(buffer); +} + +int message_state_load_res(message_state_t *state, int module) +{ + if (state->module == module) + return 1; + + state->module = module; + state->current_res = scir_find_resource(state->resmgr, sci_message, module, 0); + + if (state->current_res == NULL || + state->current_res->data == NULL) + { + sciprintf("Message subsystem: Failed to load %d.MSG\n", module); + return 0; + } + + state->record_count = state->handler->index_record_count(state->current_res->data); + state->index_records = state->current_res->data + state->handler->header_size; + + index_record_cursor_initialize(state, &state->engine_cursor); + return 1; +} + +static message_handler_t fixed_handler = {3411, + index_record_parse_3411, + index_record_get_talker_3411, + index_record_get_text_3411, + header_get_index_record_count_3411, + + 10, + 11}; + +void message_state_initialize(resource_mgr_t *resmgr, message_state_t *state) +{ + resource_t *tester = scir_find_resource(resmgr, sci_message, 0, 0); + int version; + +// if (tester == NULL) return; + +// version = getUInt16(tester->data); + + state->initialized = 1; + state->module = -1; + state->resmgr = resmgr; + state->current_res = NULL; + state->record_count = 0; + state->handler = &fixed_handler; +} diff --git a/engines/sci/engine/message.h b/engines/sci/engine/message.h new file mode 100644 index 0000000000..7618e55429 --- /dev/null +++ b/engines/sci/engine/message.h @@ -0,0 +1,83 @@ +/*************************************************************************** + message.h Copyright (C) 2008 Lars Skovlund + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Lars Skovlund (LS) [lskovlun@image.dk] + +***************************************************************************/ + +#include + +typedef struct +{ + int noun; + int verb; + int cond; + int seq; +} message_tuple_t; + +typedef struct +{ + byte *index_record; + int index; + byte *resource_beginning; +} index_record_cursor_t; + +typedef int index_record_size_t(void); +typedef void parse_index_record_t(index_record_cursor_t *index_record, message_tuple_t *t); +typedef int get_talker_t(index_record_cursor_t *cursor); +typedef void get_text_t(index_record_cursor_t *cursor, char *buffer, int buffer_size); +typedef int index_record_count_t(byte *header); + +typedef struct +{ + int version_id; + parse_index_record_t *parse; + get_talker_t *get_talker; + get_text_t *get_text; + index_record_count_t *index_record_count; + + int header_size; + int index_record_size; +} message_handler_t; + +typedef struct +{ + int initialized; + message_handler_t *handler; + resource_mgr_t *resmgr; + resource_t *current_res; + int module; + int record_count; + byte *index_records; + index_record_cursor_t engine_cursor; +} message_state_t; + +int message_get_specific(message_state_t *state, message_tuple_t *t); +int message_get_next(message_state_t *state); +int message_get_talker(message_state_t *state); +int message_get_length(message_state_t *state); +int message_get_text(message_state_t *state, char *buffer, int length); +int message_state_load_res(message_state_t *state, int module); +void message_state_initialize(resource_mgr_t *resmgr, message_state_t *state); + + diff --git a/engines/sci/engine/said.c b/engines/sci/engine/said.c new file mode 100644 index 0000000000..2e90920add --- /dev/null +++ b/engines/sci/engine/said.c @@ -0,0 +1,2561 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + WGROUP = 258, + YY_COMMA = 259, + YY_AMP = 260, + YY_SLASH = 261, + YY_PARENO = 262, + YY_PARENC = 263, + YY_BRACKETSO = 264, + YY_BRACKETSC = 265, + YY_HASH = 266, + YY_LT = 267, + YY_GT = 268, + YY_BRACKETSO_LT = 269, + YY_BRACKETSO_SLASH = 270, + YY_LT_BRACKETSO = 271, + YY_LT_PARENO = 272 + }; +#endif +/* Tokens. */ +#define WGROUP 258 +#define YY_COMMA 259 +#define YY_AMP 260 +#define YY_SLASH 261 +#define YY_PARENO 262 +#define YY_PARENC 263 +#define YY_BRACKETSO 264 +#define YY_BRACKETSC 265 +#define YY_HASH 266 +#define YY_LT 267 +#define YY_GT 268 +#define YY_BRACKETSO_LT 269 +#define YY_BRACKETSO_SLASH 270 +#define YY_LT_BRACKETSO 271 +#define YY_LT_PARENO 272 + + + + +/* Copy the first part of user declarations. */ +#line 28 "said.y" + + +#include + +#define SAID_BRANCH_NULL 0 + +#define MAX_SAID_TOKENS 128 + +/* Maximum number of words to be expected in a parsed sentence */ +#define AUGMENT_MAX_WORDS 64 + + +#define ANYWORD 0xfff + +#define WORD_TYPE_BASE 0x141 +#define WORD_TYPE_REF 0x144 +#define WORD_TYPE_SYNTACTIC_SUGAR 0x145 + +#define AUGMENT_SENTENCE_PART_BRACKETS 0x152 + +/* Minor numbers */ +#define AUGMENT_SENTENCE_MINOR_MATCH_PHRASE 0x14c +#define AUGMENT_SENTENCE_MINOR_MATCH_WORD 0x153 +#define AUGMENT_SENTENCE_MINOR_RECURSE 0x144 +#define AUGMENT_SENTENCE_MINOR_PARENTHESES 0x14f + + +#undef YYDEBUG /*1*/ +/*#define SAID_DEBUG*/ +/*#define SCI_DEBUG_PARSE_TREE_AUGMENTATION*/ /* uncomment to debug parse tree augmentation*/ + + +#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION +#define scidprintf sciprintf +#else +#define scidprintf if (0) sciprintf +#endif + + +static char *said_parse_error; + +static int said_token; +static int said_tokens_nr; +static int said_tokens[MAX_SAID_TOKENS]; + +static int said_blessed; /* increminated by said_top_branch */ + +static int said_tree_pos; /* Set to 0 if we're out of space */ +#define SAID_TREE_START 4; /* Reserve space for the 4 top nodes */ + +#define VALUE_IGNORE -424242 + +static parse_tree_node_t said_tree[VOCAB_TREE_NODES]; + +typedef int wgroup_t; +typedef int tree_t; +typedef int said_spec_t; + +static tree_t +said_aug_branch(int, int, tree_t, tree_t); + +static tree_t +said_attach_branch(tree_t, tree_t); +/* +static tree_t +said_wgroup_branch(wgroup_t); +*/ +static said_spec_t +said_top_branch(tree_t); + +static tree_t +said_paren(tree_t, tree_t); + +static tree_t +said_value(int, tree_t); + +static tree_t +said_terminal(int); + + +static int +yylex(void); + +static int +yyerror(char *s) +{ + said_parse_error = sci_strdup(s); + return 1; /* Abort */ +} + + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +/* Line 216 of yacc.c. */ +#line 232 "said.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 23 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 80 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 18 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 13 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 35 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 69 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 272 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 6, 10, 15, 16, 18, 19, 21, + 24, 29, 31, 34, 39, 41, 43, 45, 49, 51, + 55, 59, 64, 70, 73, 75, 77, 79, 83, 88, + 92, 97, 100, 105, 109, 112 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 19, 0, -1, 21, 20, -1, 21, 22, 20, -1, + 21, 22, 23, 20, -1, -1, 13, -1, -1, 27, + -1, 6, 27, -1, 15, 6, 27, 10, -1, 6, + -1, 6, 27, -1, 15, 6, 27, 10, -1, 6, + -1, 3, -1, 26, -1, 9, 26, 10, -1, 24, + -1, 7, 27, 8, -1, 26, 4, 26, -1, 26, + 14, 29, 10, -1, 26, 4, 9, 26, 10, -1, + 25, 28, -1, 25, -1, 28, -1, 29, -1, 14, + 29, 10, -1, 29, 14, 29, 10, -1, 12, 24, + 30, -1, 17, 7, 27, 8, -1, 12, 26, -1, + 16, 9, 26, 10, -1, 12, 26, 30, -1, 12, + 26, -1, 17, 7, 27, 8, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint8 yyrline[] = +{ + 0, 138, 138, 140, 142, 148, 149, 156, 157, 163, + 165, 167, 173, 175, 177, 183, 188, 190, 195, 197, + 199, 201, 203, 209, 211, 213, 219, 221, 223, 229, + 231, 233, 235, 241, 243, 245 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "WGROUP", "YY_COMMA", "YY_AMP", + "YY_SLASH", "YY_PARENO", "YY_PARENC", "YY_BRACKETSO", "YY_BRACKETSC", + "YY_HASH", "YY_LT", "YY_GT", "YY_BRACKETSO_LT", "YY_BRACKETSO_SLASH", + "YY_LT_BRACKETSO", "YY_LT_PARENO", "$accept", "saidspec", "optcont", + "leftspec", "midspec", "rightspec", "word", "cwordset", "wordset", + "expr", "cwordrefset", "wordrefset", "recref", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 18, 19, 19, 19, 20, 20, 21, 21, 22, + 22, 22, 23, 23, 23, 24, 25, 25, 26, 26, + 26, 26, 26, 27, 27, 27, 28, 28, 28, 29, + 29, 29, 29, 30, 30, 30 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 2, 3, 4, 0, 1, 0, 1, 2, + 4, 1, 2, 4, 1, 1, 1, 3, 1, 3, + 3, 4, 5, 2, 1, 1, 1, 3, 4, 3, + 4, 2, 4, 3, 2, 4 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 7, 15, 0, 0, 0, 0, 0, 0, 0, 5, + 18, 24, 16, 8, 25, 26, 0, 0, 18, 31, + 0, 0, 0, 1, 11, 6, 0, 2, 5, 23, + 0, 0, 0, 19, 17, 0, 0, 29, 27, 0, + 0, 9, 0, 14, 0, 3, 5, 0, 20, 0, + 0, 34, 0, 32, 30, 0, 12, 0, 4, 0, + 21, 28, 33, 0, 10, 0, 22, 35, 13 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 8, 27, 9, 28, 46, 10, 11, 12, 13, + 14, 15, 37 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -24 +static const yytype_int8 yypact[] = +{ + -1, -24, -1, 62, 62, 54, 1, 5, 18, 38, + -24, 47, 3, -24, -24, 12, 23, 15, -3, 3, + 28, 62, -1, -24, -1, -24, 42, -24, 39, -24, + 53, 54, 54, -24, -24, 62, 50, -24, -24, 29, + 41, -24, -1, -1, 52, -24, 55, 62, 3, 57, + 63, 20, -1, -24, -24, 64, -24, -1, -24, 32, + -24, -24, -24, 67, -24, 66, -24, -24, -24 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -24, -24, -23, -24, -24, -24, 68, -24, 0, -2, + 69, -4, 26 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 16, 20, 1, 17, 19, 45, 2, 30, 3, 35, + 21, 4, 22, 5, 36, 6, 7, 31, 23, 30, + 40, 39, 41, 58, 30, 34, 32, 49, 50, 31, + 48, 33, 35, 30, 31, 51, 30, 36, 38, 53, + 55, 56, 66, 31, 24, 43, 31, 59, 42, 54, + 63, 25, 25, 26, 44, 65, 1, 52, 57, 4, + 2, 5, 47, 6, 7, 1, 4, 60, 25, 2, + 6, 7, 18, 61, 64, 67, 68, 62, 0, 0, + 29 +}; + +static const yytype_int8 yycheck[] = +{ + 2, 5, 3, 3, 4, 28, 7, 4, 9, 12, + 9, 12, 7, 14, 17, 16, 17, 14, 0, 4, + 22, 21, 24, 46, 4, 10, 14, 31, 32, 14, + 30, 8, 12, 4, 14, 35, 4, 17, 10, 10, + 42, 43, 10, 14, 6, 6, 14, 47, 6, 8, + 52, 13, 13, 15, 15, 57, 3, 7, 6, 12, + 7, 14, 9, 16, 17, 3, 12, 10, 13, 7, + 16, 17, 4, 10, 10, 8, 10, 51, -1, -1, + 11 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 3, 7, 9, 12, 14, 16, 17, 19, 21, + 24, 25, 26, 27, 28, 29, 27, 26, 24, 26, + 29, 9, 7, 0, 6, 13, 15, 20, 22, 28, + 4, 14, 14, 8, 10, 12, 17, 30, 10, 26, + 27, 27, 6, 6, 15, 20, 23, 9, 26, 29, + 29, 26, 7, 10, 8, 27, 27, 6, 20, 26, + 10, 10, 30, 27, 10, 27, 10, 8, 10 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + fprintf (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + +/* The look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 139 "said.y" + { (yyval) = said_top_branch(said_attach_branch((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)]))); } + break; + + case 3: +#line 141 "said.y" + { (yyval) = said_top_branch(said_attach_branch((yyvsp[(1) - (3)]), said_attach_branch((yyvsp[(2) - (3)]), (yyvsp[(3) - (3)])))); } + break; + + case 4: +#line 143 "said.y" + { (yyval) = said_top_branch(said_attach_branch((yyvsp[(1) - (4)]), said_attach_branch((yyvsp[(2) - (4)]), said_attach_branch((yyvsp[(3) - (4)]), (yyvsp[(4) - (4)]))))); } + break; + + case 5: +#line 148 "said.y" + { (yyval) = SAID_BRANCH_NULL; } + break; + + case 6: +#line 150 "said.y" + { (yyval) = said_paren(said_value(0x14b, said_value(0xf900, said_terminal(0xf900))), SAID_BRANCH_NULL); } + break; + + case 7: +#line 156 "said.y" + { (yyval) = SAID_BRANCH_NULL; } + break; + + case 8: +#line 158 "said.y" + { (yyval) = said_paren(said_value(0x141, said_value(0x149, (yyvsp[(1) - (1)]))), SAID_BRANCH_NULL); } + break; + + case 9: +#line 164 "said.y" + { (yyval) = said_aug_branch(0x142, 0x14a, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL); } + break; + + case 10: +#line 166 "said.y" + { (yyval) = said_aug_branch(0x152, 0x142, said_aug_branch(0x142, 0x14a, (yyvsp[(3) - (4)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); } + break; + + case 11: +#line 168 "said.y" + { (yyval) = SAID_BRANCH_NULL; } + break; + + case 12: +#line 174 "said.y" + { (yyval) = said_aug_branch(0x143, 0x14a, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL); } + break; + + case 13: +#line 176 "said.y" + { (yyval) = said_aug_branch(0x152, 0x143, said_aug_branch(0x143, 0x14a, (yyvsp[(3) - (4)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); } + break; + + case 14: +#line 178 "said.y" + { (yyval) = SAID_BRANCH_NULL; } + break; + + case 15: +#line 184 "said.y" + { (yyval) = said_paren(said_value(0x141, said_value(0x153, said_terminal((yyvsp[(1) - (1)])))), SAID_BRANCH_NULL); } + break; + + case 16: +#line 189 "said.y" + { (yyval) = said_aug_branch(0x141, 0x14f, (yyvsp[(1) - (1)]), SAID_BRANCH_NULL); } + break; + + case 17: +#line 191 "said.y" + { (yyval) = said_aug_branch(0x141, 0x14f, said_aug_branch(0x152, 0x14c, said_aug_branch(0x141, 0x14f, (yyvsp[(2) - (3)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL), SAID_BRANCH_NULL); } + break; + + case 18: +#line 196 "said.y" + { (yyval) = (yyvsp[(1) - (1)]); } + break; + + case 19: +#line 198 "said.y" + { (yyval) = (yyvsp[(1) - (3)]); } + break; + + case 20: +#line 200 "said.y" + { (yyval) = said_attach_branch((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } + break; + + case 21: +#line 202 "said.y" + { (yyval) = said_attach_branch((yyvsp[(1) - (4)]), (yyvsp[(3) - (4)])); } + break; + + case 22: +#line 204 "said.y" + { (yyval) = said_attach_branch((yyvsp[(1) - (5)]), (yyvsp[(3) - (5)])); } + break; + + case 23: +#line 210 "said.y" + { (yyval) = said_attach_branch((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)])); } + break; + + case 24: +#line 212 "said.y" + { (yyval) = (yyvsp[(1) - (1)]); } + break; + + case 25: +#line 214 "said.y" + { (yyval) = (yyvsp[(1) - (1)]); } + break; + + case 26: +#line 220 "said.y" + { (yyval) = (yyvsp[(1) - (1)]); } + break; + + case 27: +#line 222 "said.y" + { (yyval) = said_aug_branch(0x152, 0x144, (yyvsp[(2) - (3)]), SAID_BRANCH_NULL); } + break; + + case 28: +#line 224 "said.y" + { (yyval) = said_attach_branch((yyvsp[(1) - (4)]), said_aug_branch(0x152, 0x144, (yyvsp[(3) - (4)]), SAID_BRANCH_NULL)); } + break; + + case 29: +#line 230 "said.y" + { (yyval) = said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (3)]), (yyvsp[(3) - (3)])); } + break; + + case 30: +#line 232 "said.y" + { (yyval) = said_aug_branch(0x144, 0x14f, said_aug_branch(0x141, 0x144, (yyvsp[(2) - (4)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); } + break; + + case 31: +#line 234 "said.y" + { (yyval) = said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL); } + break; + + case 32: +#line 236 "said.y" + { (yyval) = said_aug_branch(0x152, 0x144, said_aug_branch(0x144, 0x14f, (yyvsp[(3) - (4)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); } + break; + + case 33: +#line 242 "said.y" + { (yyval) = said_aug_branch(0x141, 0x144, said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (3)]), SAID_BRANCH_NULL), (yyvsp[(3) - (3)])); } + break; + + case 34: +#line 244 "said.y" + { (yyval) = said_aug_branch(0x141, 0x144, said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); } + break; + + case 35: +#line 246 "said.y" + { (yyval) = said_aug_branch(0x141, 0x14c, (yyvsp[(2) - (4)]), SAID_BRANCH_NULL); } + break; + + +/* Line 1267 of yacc.c. */ +#line 1647 "said.c" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (yymsg); + } + else + { + yyerror (YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + +#line 251 "said.y" + + + +int +parse_yy_token_lookup[] = {YY_COMMA, YY_AMP, YY_SLASH, YY_PARENO, YY_PARENC, YY_BRACKETSO, YY_BRACKETSC, + YY_HASH, YY_LT, YY_GT}; + +static int +yylex(void) +{ + int retval = said_tokens[said_token++]; + + if (retval < SAID_LONG(SAID_FIRST)) { + yylval = retval; + retval = WGROUP; + } else { + retval >>= 8; + + if (retval == SAID_TERM) + retval = 0; + else { + assert(retval >= SAID_FIRST); + retval = parse_yy_token_lookup[retval - SAID_FIRST]; + if (retval == YY_BRACKETSO) { + if ((said_tokens[said_token] >> 8) == SAID_LT) + retval = YY_BRACKETSO_LT; + else + if ((said_tokens[said_token] >> 8) == SAID_SLASH) + retval = YY_BRACKETSO_SLASH; + } else if (retval == YY_LT && (said_tokens[said_token] >> 8) == SAID_BRACKO) { + retval = YY_LT_BRACKETSO; + } else if (retval == YY_LT && (said_tokens[said_token] >> 8) == SAID_PARENO) { + retval = YY_LT_PARENO; + } + } + } + + return retval; +} + +#define SAID_NEXT_NODE ((said_tree_pos == 0) || (said_tree_pos >= VOCAB_TREE_NODES))? said_tree_pos = 0 : said_tree_pos++ + +static inline int +said_leaf_node(tree_t pos, int value) +{ + said_tree[pos].type = PARSE_TREE_NODE_LEAF; + + if (value != VALUE_IGNORE) + said_tree[pos].content.value = value; + + return pos; +} + +static inline int +said_branch_node(tree_t pos, int left, int right) +{ + said_tree[pos].type = PARSE_TREE_NODE_BRANCH; + + if (left != VALUE_IGNORE) + said_tree[pos].content.branches[0] = left; + + if (right != VALUE_IGNORE) + said_tree[pos].content.branches[1] = right; + + return pos; +} + + +static tree_t +said_paren(tree_t t1, tree_t t2) +{ + if (t1) + return said_branch_node(SAID_NEXT_NODE, + t1, + t2 + ); + else + return t2; +} + +static tree_t +said_value(int val, tree_t t) +{ + return said_branch_node(SAID_NEXT_NODE, + said_leaf_node(SAID_NEXT_NODE, val), + t + ); + +} + +static tree_t +said_terminal(int val) +{ + return said_leaf_node(SAID_NEXT_NODE, val); +} + + +static tree_t +said_aug_branch(int n1, int n2, tree_t t1, tree_t t2) +{ + int retval; + + retval = said_branch_node(SAID_NEXT_NODE, + said_branch_node(SAID_NEXT_NODE, + said_leaf_node(SAID_NEXT_NODE, n1), + said_branch_node(SAID_NEXT_NODE, + said_leaf_node(SAID_NEXT_NODE, n2), + t1 + ) + ), + t2 + ); + +#ifdef SAID_DEBUG + fprintf(stderr,"AUG(0x%x, 0x%x, [%04x], [%04x]) = [%04x]\n", n1, n2, t1, t2, retval); +#endif + + return retval; +} + +static tree_t +said_attach_branch(tree_t base, tree_t attacheant) +{ +#ifdef SAID_DEBUG + fprintf(stderr,"ATT2([%04x], [%04x]) = [%04x]\n", base, attacheant, base); +#endif + + if (!attacheant) + return base; + if (!base) + return attacheant; + + if (!base) + return 0; /* Happens if we're out of space */ + + said_branch_node(base, VALUE_IGNORE, attacheant); + + return base; +} + +static said_spec_t +said_top_branch(tree_t first) +{ +#ifdef SAID_DEBUG + fprintf(stderr, "TOP([%04x])\n", first); +#endif + said_branch_node(0, 1, 2); + said_leaf_node(1, 0x141); /* Magic number #1 */ + said_branch_node(2, 3, first); + said_leaf_node(3, 0x13f); /* Magic number #2 */ + + ++said_blessed; + + return 0; +} + + +int +said_parse_spec(state_t *s, byte *spec) +{ + int nextitem; + + said_parse_error = NULL; + said_token = 0; + said_tokens_nr = 0; + said_blessed = 0; + + said_tree_pos = SAID_TREE_START; + + do { + nextitem = *spec++; + if (nextitem < SAID_FIRST) + said_tokens[said_tokens_nr++] = nextitem << 8 | *spec++; + else + said_tokens[said_tokens_nr++] = SAID_LONG(nextitem); + + } while ((nextitem != SAID_TERM) && (said_tokens_nr < MAX_SAID_TOKENS)); + + if (nextitem == SAID_TERM) + yyparse(); + else { + sciprintf("Error: SAID spec is too long\n"); + return 1; + } + + if (said_parse_error) { + sciprintf("Error while parsing SAID spec: %s\n", said_parse_error); + free(said_parse_error); + return 1; + } + + if (said_tree_pos == 0) { + sciprintf("Error: Out of tree space while parsing SAID spec\n"); + return 1; + } + + if (said_blessed != 1) { + sciprintf("Error: Found %d top branches\n"); + return 1; + } + + return 0; +} + +/**********************/ +/**** Augmentation ****/ +/**********************/ + + +/** primitive functions **/ + +#define AUG_READ_BRANCH(a, br, p) \ + if (tree[p].type != PARSE_TREE_NODE_BRANCH) \ + return 0; \ + a = tree[p].content.branches[br]; + +#define AUG_READ_VALUE(a, p) \ + if (tree[p].type != PARSE_TREE_NODE_LEAF) \ + return 0; \ + a = tree[p].content.value; + +#define AUG_ASSERT(i) \ + if (!i) return 0; + +static int +aug_get_next_sibling(parse_tree_node_t *tree, int pos, int *first, int *second) + /* Returns the next sibling relative to the specified position in 'tree', + ** sets *first and *second to its augment node values, returns the new position + ** or 0 if there was no next sibling + */ +{ + int seek, valpos; + + AUG_READ_BRANCH(pos, 1, pos); + AUG_ASSERT(pos); + AUG_READ_BRANCH(seek, 0, pos); + AUG_ASSERT(seek); + + /* Now retreive first value */ + AUG_READ_BRANCH(valpos, 0, seek); + AUG_ASSERT(valpos); + AUG_READ_VALUE(*first, valpos); + + /* Get second value */ + AUG_READ_BRANCH(seek, 1, seek); + AUG_ASSERT(seek); + AUG_READ_BRANCH(valpos, 0, seek); + AUG_ASSERT(valpos); + AUG_READ_VALUE(*second, valpos); + + return pos; +} + + +static int +aug_get_wgroup(parse_tree_node_t *tree, int pos) + /* Returns 0 if pos in tree is not the root of a 3-element list, otherwise + ** it returns the last element (which, in practice, is the word group + */ +{ + int val; + + AUG_READ_BRANCH(pos, 0, pos); + AUG_ASSERT(pos); + AUG_READ_BRANCH(pos, 1, pos); + AUG_ASSERT(pos); + AUG_READ_BRANCH(pos, 1, pos); + AUG_ASSERT(pos); + AUG_READ_VALUE(val, pos); + + return val; +} + + +static int +aug_get_base_node(parse_tree_node_t *tree) +{ + int startpos = 0; + AUG_READ_BRANCH(startpos, 1, startpos); + return startpos; +} + + +/** semi-primitive functions **/ + + +static int +aug_get_first_child(parse_tree_node_t *tree, int pos, int *first, int *second) + /* like aug_get_next_sibling, except that it recurses into the tree and + ** finds the first child (usually *not* Ayanami Rei) of the current branch + ** rather than its next sibling. + */ +{ + AUG_READ_BRANCH(pos, 0, pos); + AUG_ASSERT(pos); + AUG_READ_BRANCH(pos, 1, pos); + AUG_ASSERT(pos); + + return aug_get_next_sibling(tree, pos, first, second); +} + +static void +aug_find_words_recursively(parse_tree_node_t *tree, int startpos, + int *base_words, int *base_words_nr, + int *ref_words, int *ref_words_nr, + int maxwords, int refbranch) + /* Finds and lists all base (141) and reference (144) words */ +{ + int major, minor; + int word; + int pos = aug_get_first_child(tree, startpos, &major, &minor); + + /* if (major == WORD_TYPE_REF) + refbranch = 1;*/ + + while (pos) { + if ((word = aug_get_wgroup(tree, pos))) { /* found a word */ + + if (!refbranch && major == WORD_TYPE_BASE) { + if ((*base_words_nr) == maxwords) { + sciprintf("Out of regular words\n"); + return; /* return gracefully */ + } + + base_words[*base_words_nr] = word; /* register word */ + ++(*base_words_nr); + + } + if (major == WORD_TYPE_REF || refbranch) { + if ((*ref_words_nr) == maxwords) { + sciprintf("Out of reference words\n"); + return; /* return gracefully */ + } + + ref_words[*ref_words_nr] = word; /* register word */ + ++(*ref_words_nr); + + } + if (major != WORD_TYPE_SYNTACTIC_SUGAR && major != WORD_TYPE_BASE && major != WORD_TYPE_REF) + sciprintf("aug_find_words_recursively(): Unknown word type %03x\n", major); + + } else /* Did NOT find a word group: Attempt to recurse */ + aug_find_words_recursively(tree, pos, base_words, base_words_nr, + ref_words, ref_words_nr, maxwords, refbranch || major == WORD_TYPE_REF); + + pos = aug_get_next_sibling(tree, pos, &major, &minor); + } +} + + +static void +aug_find_words(parse_tree_node_t *tree, int startpos, + int *base_words, int *base_words_nr, + int *ref_words, int *ref_words_nr, + int maxwords) + /* initializing wrapper for aug_find_words_recursively() */ +{ + *base_words_nr = 0; + *ref_words_nr = 0; + + aug_find_words_recursively(tree, startpos, base_words, base_words_nr, ref_words, ref_words_nr, maxwords, 0); +} + + +static inline int +aug_contains_word(int *list, int length, int word) +{ + int i; + if (word == ANYWORD) + return (length); + + for (i = 0; i < length; i++) + if (list[i] == word) + return 1; + + return 0; +} + + +static int +augment_sentence_expression(parse_tree_node_t *saidt, int augment_pos, + parse_tree_node_t *parset, int parse_branch, + int major, int minor, + int *base_words, int base_words_nr, + int *ref_words, int ref_words_nr); + +static int +augment_match_expression_p(parse_tree_node_t *saidt, int augment_pos, + parse_tree_node_t *parset, int parse_basepos, + int major, int minor, + int *base_words, int base_words_nr, + int *ref_words, int ref_words_nr) +{ + int cmajor, cminor, cpos; + cpos = aug_get_first_child(saidt, augment_pos, &cmajor, &cminor); + if (!cpos) { + sciprintf("augment_match_expression_p(): Empty condition\n"); + return 1; + } + + scidprintf("Attempting to match (%03x %03x (%03x %03x\n", major, minor, cmajor, cminor); + + if ((major == WORD_TYPE_BASE) && (minor == AUGMENT_SENTENCE_MINOR_RECURSE)) + return augment_match_expression_p(saidt, cpos, + parset, parse_basepos, + cmajor, cminor, + base_words, base_words_nr, + ref_words, ref_words_nr); + + + switch (major) { + + case WORD_TYPE_BASE: + while (cpos) { + if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_WORD) { + int word = aug_get_wgroup(saidt, cpos); + scidprintf("Looking for word %03x\n", word); + + if (aug_contains_word(base_words, base_words_nr, word)) + return 1; + } else if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_PHRASE) { + if (augment_sentence_expression(saidt, cpos, + parset, parse_basepos, + cmajor, cminor, + base_words, base_words_nr, + ref_words, ref_words_nr)) + return 1; + } else if (cminor == AUGMENT_SENTENCE_MINOR_PARENTHESES) { + int gc_major, gc_minor; + int gchild = aug_get_first_child(saidt, cpos, &gc_major, &gc_minor); + + while (gchild) { + if (augment_match_expression_p(saidt, cpos, + parset, parse_basepos, + major, minor, + base_words, base_words_nr, + ref_words, ref_words_nr)) + return 1; + gchild = aug_get_next_sibling(saidt, gchild, &gc_major, &gc_minor); + } + } else + sciprintf("augment_match_expression_p(): Unknown type 141 minor number %3x\n", cminor); + + cpos = aug_get_next_sibling(saidt, cpos, &cmajor, &cminor); + + } + break; + + case WORD_TYPE_REF: + while (cpos) { + if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_WORD) { + int word = aug_get_wgroup(saidt, cpos); + scidprintf("Looking for refword %03x\n", word); + + if (aug_contains_word(ref_words, ref_words_nr, word)) + return 1; + } else if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_PHRASE) { + if (augment_match_expression_p(saidt, cpos, + parset, parse_basepos, + cmajor, cminor, + base_words, base_words_nr, + ref_words, ref_words_nr)) + return 1; + } else if (cminor == AUGMENT_SENTENCE_MINOR_PARENTHESES) { + int gc_major, gc_minor; + int gchild = aug_get_first_child(saidt, cpos, &gc_major, &gc_minor); + + while (gchild) { + if (augment_match_expression_p(saidt, cpos, + parset, parse_basepos, + major, minor, + base_words, base_words_nr, + ref_words, ref_words_nr)) + return 1; + gchild = aug_get_next_sibling(saidt, gchild, &gc_major, &gc_minor); + } + } else + sciprintf("augment_match_expression_p(): Unknown type 144 minor number %3x\n", cminor); + + cpos = aug_get_next_sibling(saidt, cpos, &cmajor, &cminor); + + } + break; + + case AUGMENT_SENTENCE_PART_BRACKETS: + if (augment_match_expression_p(saidt, cpos, + parset, parse_basepos, + cmajor, cminor, + base_words, base_words_nr, + ref_words, ref_words_nr)) + return 1; + + scidprintf("Didn't match subexpression; checking sub-bracked predicate %03x\n", cmajor); + + switch (cmajor) { + case WORD_TYPE_BASE: + if (!base_words_nr) + return 1; + break; + + case WORD_TYPE_REF: + if (!ref_words_nr) + return 1; + break; + + default: + sciprintf("augment_match_expression_p(): (subp1) Unkonwn sub-bracket predicate %03x\n", cmajor); + } + + break; + + default: + sciprintf("augment_match_expression_p(): Unknown predicate %03x\n", major); + + } + + scidprintf("Generic failure\n"); + return 0; +} + +static int +augment_sentence_expression(parse_tree_node_t *saidt, int augment_pos, + parse_tree_node_t *parset, int parse_branch, + int major, int minor, + int *base_words, int base_words_nr, + int *ref_words, int ref_words_nr) +{ + int check_major, check_minor; + int check_pos = aug_get_first_child(saidt, augment_pos, &check_major, &check_minor); + do { + if (!(augment_match_expression_p(saidt, check_pos, parset, parse_branch, + check_major, check_minor, base_words, base_words_nr, + ref_words, ref_words_nr))) + return 0; + } while ((check_pos = aug_get_next_sibling(saidt, check_pos, &check_major, &check_minor))); + return 1; +} + + + +static int +augment_sentence_part(parse_tree_node_t *saidt, int augment_pos, + parse_tree_node_t *parset, int parse_basepos, + int major, int minor) +{ + int pmajor, pminor; + int parse_branch = parse_basepos; + int optional = 0; + int foundwords = 0; + + scidprintf("Augmenting (%03x %03x\n", major, minor); + + if (major == AUGMENT_SENTENCE_PART_BRACKETS) { /* '[/ foo]' is true if '/foo' or if there + ** exists no x for which '/x' is true + */ + if ((augment_pos = aug_get_first_child(saidt, augment_pos, &major, &minor))) { + scidprintf("Optional part: Now augmenting (%03x %03x\n", major, minor); + optional = 1; + } else { + scidprintf("Matched empty optional expression\n"); + return 1; + } + } + + if ((major < 0x141) + || (major > 0x143)) { + scidprintf("augment_sentence_part(): Unexpected sentence part major number %03x\n", major); + return 0; + } + + while ((parse_branch = aug_get_next_sibling(parset, parse_branch, &pmajor, &pminor))) + if (pmajor == major) { /* found matching sentence part */ + int success; + int base_words_nr; + int ref_words_nr; + int base_words[AUGMENT_MAX_WORDS]; + int ref_words[AUGMENT_MAX_WORDS]; +#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION + int i; +#endif + + scidprintf("Found match with pminor = %03x\n", pminor); + aug_find_words(parset, parse_branch, base_words, &base_words_nr, + ref_words, &ref_words_nr, AUGMENT_MAX_WORDS); + foundwords |= (ref_words_nr | base_words_nr); +#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION + sciprintf("%d base words:", base_words_nr); + for (i = 0; i < base_words_nr; i++) + sciprintf(" %03x", base_words[i]); + sciprintf("\n%d reference words:", ref_words_nr); + for (i = 0; i < ref_words_nr; i++) + sciprintf(" %03x", ref_words[i]); + sciprintf("\n"); +#endif + + success = augment_sentence_expression(saidt, augment_pos, + parset, parse_basepos, major, minor, + base_words, base_words_nr, + ref_words, ref_words_nr); + + if (success) { + scidprintf("SUCCESS on augmenting (%03x %03x\n", major, minor); + return 1; + } + } + + if (optional && (foundwords == 0)) { + scidprintf("Found no words and optional branch => SUCCESS on augmenting (%03x %03x\n", major, minor); + return 1; + } + scidprintf("FAILURE on augmenting (%03x %03x\n", major, minor); + return 0; +} + +static int +augment_parse_nodes(parse_tree_node_t *parset, parse_tree_node_t *saidt) +{ + int augment_basepos = 0; + int parse_basepos; + int major, minor; + int dontclaim = 0; + + parse_basepos = aug_get_base_node(parset); + if (!parse_basepos) { + sciprintf("augment_parse_nodes(): Parse tree is corrupt\n"); + return 0; + } + + augment_basepos = aug_get_base_node(saidt); + if (!augment_basepos) { + sciprintf("augment_parse_nodes(): Said tree is corrupt\n"); + return 0; + } + while ((augment_basepos = aug_get_next_sibling(saidt, augment_basepos, &major, &minor))) { + + if ((major == 0x14b) + && (minor == SAID_LONG(SAID_GT))) + dontclaim = 1; /* special case */ + else /* normal sentence part */ + if (!(augment_sentence_part(saidt, augment_basepos, parset, parse_basepos, major, minor))) { + scidprintf("Returning failure\n"); + return 0; /* fail */ + } + } + + scidprintf("Returning success with dontclaim=%d\n", dontclaim); + + if (dontclaim) + return SAID_PARTIAL_MATCH; + else return 1; /* full match */ +} + + +/*******************/ +/**** Main code ****/ +/*******************/ + +int +said(state_t *s, byte *spec, int verbose) +{ + int retval; + + parse_tree_node_t *parse_tree_ptr = s->parser_nodes; + + if (s->parser_valid) { + + if (said_parse_spec(s, spec)) { + sciprintf("Offending spec was: "); + vocab_decypher_said_block(s, spec); + return SAID_NO_MATCH; + } + + if (verbose) + vocab_dump_parse_tree("Said-tree", said_tree); /* Nothing better to do yet */ + retval = augment_parse_nodes(parse_tree_ptr, &(said_tree[0])); + + if (!retval) + return SAID_NO_MATCH; + else if (retval != SAID_PARTIAL_MATCH) + return SAID_FULL_MATCH; + else return SAID_PARTIAL_MATCH; + } + + return SAID_NO_MATCH; +} + + + +#ifdef SAID_DEBUG_PROGRAM +int +main (int argc, char *argv) +{ + byte block[] = {0x01, 0x00, 0xf8, 0xf5, 0x02, 0x01, 0xf6, 0xf2, 0x02, 0x01, 0xf2, 0x01, 0x03, 0xff}; + state_t s; + con_passthrough = 1; + + s.parser_valid = 1; + said(&s, block); +} +#endif + diff --git a/engines/sci/engine/said.y b/engines/sci/engine/said.y new file mode 100644 index 0000000000..fdef2360ae --- /dev/null +++ b/engines/sci/engine/said.y @@ -0,0 +1,950 @@ +/*************************************************************************** + said.y Copyright (C) 1999 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + +%{ + +#include + +#define SAID_BRANCH_NULL 0 + +#define MAX_SAID_TOKENS 128 + +/* Maximum number of words to be expected in a parsed sentence */ +#define AUGMENT_MAX_WORDS 64 + + +#define ANYWORD 0xfff + +#define WORD_TYPE_BASE 0x141 +#define WORD_TYPE_REF 0x144 +#define WORD_TYPE_SYNTACTIC_SUGAR 0x145 + +#define AUGMENT_SENTENCE_PART_BRACKETS 0x152 + +/* Minor numbers */ +#define AUGMENT_SENTENCE_MINOR_MATCH_PHRASE 0x14c +#define AUGMENT_SENTENCE_MINOR_MATCH_WORD 0x153 +#define AUGMENT_SENTENCE_MINOR_RECURSE 0x144 +#define AUGMENT_SENTENCE_MINOR_PARENTHESES 0x14f + + +#undef YYDEBUG /*1*/ +/*#define SAID_DEBUG*/ +/*#define SCI_DEBUG_PARSE_TREE_AUGMENTATION*/ /* uncomment to debug parse tree augmentation*/ + + +#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION +#define scidprintf sciprintf +#else +#define scidprintf if (0) sciprintf +#endif + + +static char *said_parse_error; + +static int said_token; +static int said_tokens_nr; +static int said_tokens[MAX_SAID_TOKENS]; + +static int said_blessed; /* increminated by said_top_branch */ + +static int said_tree_pos; /* Set to 0 if we're out of space */ +#define SAID_TREE_START 4; /* Reserve space for the 4 top nodes */ + +#define VALUE_IGNORE -424242 + +static parse_tree_node_t said_tree[VOCAB_TREE_NODES]; + +typedef int wgroup_t; +typedef int tree_t; +typedef int said_spec_t; + +static tree_t +said_aug_branch(int, int, tree_t, tree_t); + +static tree_t +said_attach_branch(tree_t, tree_t); +/* +static tree_t +said_wgroup_branch(wgroup_t); +*/ +static said_spec_t +said_top_branch(tree_t); + +static tree_t +said_paren(tree_t, tree_t); + +static tree_t +said_value(int, tree_t); + +static tree_t +said_terminal(int); + + +static int +yylex(void); + +static int +yyerror(char *s) +{ + said_parse_error = sci_strdup(s); + return 1; /* Abort */ +} + +%} + +%token WGROUP /* Word group */ +%token YY_COMMA /* 0xf0 */ +%token YY_AMP /* 0xf1 */ +%token YY_SLASH /* 0xf2 */ +%token YY_PARENO /* 0xf3 */ +%token YY_PARENC /* 0xf4 */ +%token YY_BRACKETSO /* 0xf5 */ +%token YY_BRACKETSC /* 0xf6 */ +%token YY_HASH /* 0xf7 */ +%token YY_LT /* 0xf8 */ +%token YY_GT /* 0xf9 */ +%token YY_BRACKETSO_LT /* special token used to imitate LR(2) behaviour */ +%token YY_BRACKETSO_SLASH /* special token used to imitate LR(2) behaviour */ +%token YY_LT_BRACKETSO /* special token used to imitate LR(2) behaviour */ +%token YY_LT_PARENO /* special token used to imitate LR(2) behaviour */ + +%% + +saidspec : leftspec optcont + { $$ = said_top_branch(said_attach_branch($1, $2)); } + | leftspec midspec optcont + { $$ = said_top_branch(said_attach_branch($1, said_attach_branch($2, $3))); } + | leftspec midspec rightspec optcont + { $$ = said_top_branch(said_attach_branch($1, said_attach_branch($2, said_attach_branch($3, $4)))); } + ; + + +optcont : /* empty */ + { $$ = SAID_BRANCH_NULL; } + | YY_GT + { $$ = said_paren(said_value(0x14b, said_value(0xf900, said_terminal(0xf900))), SAID_BRANCH_NULL); } + ; + + + +leftspec : /* empty */ + { $$ = SAID_BRANCH_NULL; } + | expr + { $$ = said_paren(said_value(0x141, said_value(0x149, $1)), SAID_BRANCH_NULL); } + ; + + + +midspec : YY_SLASH expr + { $$ = said_aug_branch(0x142, 0x14a, $2, SAID_BRANCH_NULL); } + | YY_BRACKETSO_SLASH YY_SLASH expr YY_BRACKETSC + { $$ = said_aug_branch(0x152, 0x142, said_aug_branch(0x142, 0x14a, $3, SAID_BRANCH_NULL), SAID_BRANCH_NULL); } + | YY_SLASH + { $$ = SAID_BRANCH_NULL; } + ; + + + +rightspec : YY_SLASH expr + { $$ = said_aug_branch(0x143, 0x14a, $2, SAID_BRANCH_NULL); } + | YY_BRACKETSO_SLASH YY_SLASH expr YY_BRACKETSC + { $$ = said_aug_branch(0x152, 0x143, said_aug_branch(0x143, 0x14a, $3, SAID_BRANCH_NULL), SAID_BRANCH_NULL); } + | YY_SLASH + { $$ = SAID_BRANCH_NULL; } + ; + + + +word : WGROUP + { $$ = said_paren(said_value(0x141, said_value(0x153, said_terminal($1))), SAID_BRANCH_NULL); } + ; + + +cwordset : wordset + { $$ = said_aug_branch(0x141, 0x14f, $1, SAID_BRANCH_NULL); } + | YY_BRACKETSO wordset YY_BRACKETSC + { $$ = said_aug_branch(0x141, 0x14f, said_aug_branch(0x152, 0x14c, said_aug_branch(0x141, 0x14f, $2, SAID_BRANCH_NULL), SAID_BRANCH_NULL), SAID_BRANCH_NULL); } + ; + + +wordset : word + { $$ = $1; } + | YY_PARENO expr YY_PARENC + { $$ = $1; } + | wordset YY_COMMA wordset + { $$ = said_attach_branch($1, $3); } + | wordset YY_BRACKETSO_LT wordrefset YY_BRACKETSC + { $$ = said_attach_branch($1, $3); } + | wordset YY_COMMA YY_BRACKETSO wordset YY_BRACKETSC + { $$ = said_attach_branch($1, $3); } + ; + + + +expr : cwordset cwordrefset + { $$ = said_attach_branch($1, $2); } + | cwordset + { $$ = $1; } + | cwordrefset + { $$ = $1; } + ; + + + +cwordrefset : wordrefset + { $$ = $1; } + | YY_BRACKETSO_LT wordrefset YY_BRACKETSC + { $$ = said_aug_branch(0x152, 0x144, $2, SAID_BRANCH_NULL); } + | wordrefset YY_BRACKETSO_LT wordrefset YY_BRACKETSC + { $$ = said_attach_branch($1, said_aug_branch(0x152, 0x144, $3, SAID_BRANCH_NULL)); } + ; + + + +wordrefset : YY_LT word recref + { $$ = said_aug_branch(0x144, 0x14f, $2, $3); } + | YY_LT_PARENO YY_PARENO expr YY_PARENC + { $$ = said_aug_branch(0x144, 0x14f, said_aug_branch(0x141, 0x144, $2, SAID_BRANCH_NULL), SAID_BRANCH_NULL); } + | YY_LT wordset + { $$ = said_aug_branch(0x144, 0x14f, $2, SAID_BRANCH_NULL); } + | YY_LT_BRACKETSO YY_BRACKETSO wordset YY_BRACKETSC + { $$ = said_aug_branch(0x152, 0x144, said_aug_branch(0x144, 0x14f, $3, SAID_BRANCH_NULL), SAID_BRANCH_NULL); } + ; + + + +recref : YY_LT wordset recref + { $$ = said_aug_branch(0x141, 0x144, said_aug_branch(0x144, 0x14f, $2, SAID_BRANCH_NULL), $3); } + | YY_LT wordset + { $$ = said_aug_branch(0x141, 0x144, said_aug_branch(0x144, 0x14f, $2, SAID_BRANCH_NULL), SAID_BRANCH_NULL); } + | YY_LT_PARENO YY_PARENO expr YY_PARENC + { $$ = said_aug_branch(0x141, 0x14c, $2, SAID_BRANCH_NULL); } + ; + + + +%% + + +int +parse_yy_token_lookup[] = {YY_COMMA, YY_AMP, YY_SLASH, YY_PARENO, YY_PARENC, YY_BRACKETSO, YY_BRACKETSC, + YY_HASH, YY_LT, YY_GT}; + +static int +yylex(void) +{ + int retval = said_tokens[said_token++]; + + if (retval < SAID_LONG(SAID_FIRST)) { + yylval = retval; + retval = WGROUP; + } else { + retval >>= 8; + + if (retval == SAID_TERM) + retval = 0; + else { + assert(retval >= SAID_FIRST); + retval = parse_yy_token_lookup[retval - SAID_FIRST]; + if (retval == YY_BRACKETSO) { + if ((said_tokens[said_token] >> 8) == SAID_LT) + retval = YY_BRACKETSO_LT; + else + if ((said_tokens[said_token] >> 8) == SAID_SLASH) + retval = YY_BRACKETSO_SLASH; + } else if (retval == YY_LT && (said_tokens[said_token] >> 8) == SAID_BRACKO) { + retval = YY_LT_BRACKETSO; + } else if (retval == YY_LT && (said_tokens[said_token] >> 8) == SAID_PARENO) { + retval = YY_LT_PARENO; + } + } + } + + return retval; +} + +#define SAID_NEXT_NODE ((said_tree_pos == 0) || (said_tree_pos >= VOCAB_TREE_NODES))? said_tree_pos = 0 : said_tree_pos++ + +static inline int +said_leaf_node(tree_t pos, int value) +{ + said_tree[pos].type = PARSE_TREE_NODE_LEAF; + + if (value != VALUE_IGNORE) + said_tree[pos].content.value = value; + + return pos; +} + +static inline int +said_branch_node(tree_t pos, int left, int right) +{ + said_tree[pos].type = PARSE_TREE_NODE_BRANCH; + + if (left != VALUE_IGNORE) + said_tree[pos].content.branches[0] = left; + + if (right != VALUE_IGNORE) + said_tree[pos].content.branches[1] = right; + + return pos; +} + + +static tree_t +said_paren(tree_t t1, tree_t t2) +{ + if (t1) + return said_branch_node(SAID_NEXT_NODE, + t1, + t2 + ); + else + return t2; +} + +static tree_t +said_value(int val, tree_t t) +{ + return said_branch_node(SAID_NEXT_NODE, + said_leaf_node(SAID_NEXT_NODE, val), + t + ); + +} + +static tree_t +said_terminal(int val) +{ + return said_leaf_node(SAID_NEXT_NODE, val); +} + + +static tree_t +said_aug_branch(int n1, int n2, tree_t t1, tree_t t2) +{ + int retval; + + retval = said_branch_node(SAID_NEXT_NODE, + said_branch_node(SAID_NEXT_NODE, + said_leaf_node(SAID_NEXT_NODE, n1), + said_branch_node(SAID_NEXT_NODE, + said_leaf_node(SAID_NEXT_NODE, n2), + t1 + ) + ), + t2 + ); + +#ifdef SAID_DEBUG + fprintf(stderr,"AUG(0x%x, 0x%x, [%04x], [%04x]) = [%04x]\n", n1, n2, t1, t2, retval); +#endif + + return retval; +} + +static tree_t +said_attach_branch(tree_t base, tree_t attacheant) +{ +#ifdef SAID_DEBUG + fprintf(stderr,"ATT2([%04x], [%04x]) = [%04x]\n", base, attacheant, base); +#endif + + if (!attacheant) + return base; + if (!base) + return attacheant; + + if (!base) + return 0; /* Happens if we're out of space */ + + said_branch_node(base, VALUE_IGNORE, attacheant); + + return base; +} + +static said_spec_t +said_top_branch(tree_t first) +{ +#ifdef SAID_DEBUG + fprintf(stderr, "TOP([%04x])\n", first); +#endif + said_branch_node(0, 1, 2); + said_leaf_node(1, 0x141); /* Magic number #1 */ + said_branch_node(2, 3, first); + said_leaf_node(3, 0x13f); /* Magic number #2 */ + + ++said_blessed; + + return 0; +} + + +int +said_parse_spec(state_t *s, byte *spec) +{ + int nextitem; + + said_parse_error = NULL; + said_token = 0; + said_tokens_nr = 0; + said_blessed = 0; + + said_tree_pos = SAID_TREE_START; + + do { + nextitem = *spec++; + if (nextitem < SAID_FIRST) + said_tokens[said_tokens_nr++] = nextitem << 8 | *spec++; + else + said_tokens[said_tokens_nr++] = SAID_LONG(nextitem); + + } while ((nextitem != SAID_TERM) && (said_tokens_nr < MAX_SAID_TOKENS)); + + if (nextitem == SAID_TERM) + yyparse(); + else { + sciprintf("Error: SAID spec is too long\n"); + return 1; + } + + if (said_parse_error) { + sciprintf("Error while parsing SAID spec: %s\n", said_parse_error); + free(said_parse_error); + return 1; + } + + if (said_tree_pos == 0) { + sciprintf("Error: Out of tree space while parsing SAID spec\n"); + return 1; + } + + if (said_blessed != 1) { + sciprintf("Error: Found %d top branches\n"); + return 1; + } + + return 0; +} + +/**********************/ +/**** Augmentation ****/ +/**********************/ + + +/** primitive functions **/ + +#define AUG_READ_BRANCH(a, br, p) \ + if (tree[p].type != PARSE_TREE_NODE_BRANCH) \ + return 0; \ + a = tree[p].content.branches[br]; + +#define AUG_READ_VALUE(a, p) \ + if (tree[p].type != PARSE_TREE_NODE_LEAF) \ + return 0; \ + a = tree[p].content.value; + +#define AUG_ASSERT(i) \ + if (!i) return 0; + +static int +aug_get_next_sibling(parse_tree_node_t *tree, int pos, int *first, int *second) + /* Returns the next sibling relative to the specified position in 'tree', + ** sets *first and *second to its augment node values, returns the new position + ** or 0 if there was no next sibling + */ +{ + int seek, valpos; + + AUG_READ_BRANCH(pos, 1, pos); + AUG_ASSERT(pos); + AUG_READ_BRANCH(seek, 0, pos); + AUG_ASSERT(seek); + + /* Now retreive first value */ + AUG_READ_BRANCH(valpos, 0, seek); + AUG_ASSERT(valpos); + AUG_READ_VALUE(*first, valpos); + + /* Get second value */ + AUG_READ_BRANCH(seek, 1, seek); + AUG_ASSERT(seek); + AUG_READ_BRANCH(valpos, 0, seek); + AUG_ASSERT(valpos); + AUG_READ_VALUE(*second, valpos); + + return pos; +} + + +static int +aug_get_wgroup(parse_tree_node_t *tree, int pos) + /* Returns 0 if pos in tree is not the root of a 3-element list, otherwise + ** it returns the last element (which, in practice, is the word group + */ +{ + int val; + + AUG_READ_BRANCH(pos, 0, pos); + AUG_ASSERT(pos); + AUG_READ_BRANCH(pos, 1, pos); + AUG_ASSERT(pos); + AUG_READ_BRANCH(pos, 1, pos); + AUG_ASSERT(pos); + AUG_READ_VALUE(val, pos); + + return val; +} + + +static int +aug_get_base_node(parse_tree_node_t *tree) +{ + int startpos = 0; + AUG_READ_BRANCH(startpos, 1, startpos); + return startpos; +} + + +/** semi-primitive functions **/ + + +static int +aug_get_first_child(parse_tree_node_t *tree, int pos, int *first, int *second) + /* like aug_get_next_sibling, except that it recurses into the tree and + ** finds the first child (usually *not* Ayanami Rei) of the current branch + ** rather than its next sibling. + */ +{ + AUG_READ_BRANCH(pos, 0, pos); + AUG_ASSERT(pos); + AUG_READ_BRANCH(pos, 1, pos); + AUG_ASSERT(pos); + + return aug_get_next_sibling(tree, pos, first, second); +} + +static void +aug_find_words_recursively(parse_tree_node_t *tree, int startpos, + int *base_words, int *base_words_nr, + int *ref_words, int *ref_words_nr, + int maxwords, int refbranch) + /* Finds and lists all base (141) and reference (144) words */ +{ + int major, minor; + int word; + int pos = aug_get_first_child(tree, startpos, &major, &minor); + + /* if (major == WORD_TYPE_REF) + refbranch = 1;*/ + + while (pos) { + if ((word = aug_get_wgroup(tree, pos))) { /* found a word */ + + if (!refbranch && major == WORD_TYPE_BASE) { + if ((*base_words_nr) == maxwords) { + sciprintf("Out of regular words\n"); + return; /* return gracefully */ + } + + base_words[*base_words_nr] = word; /* register word */ + ++(*base_words_nr); + + } + if (major == WORD_TYPE_REF || refbranch) { + if ((*ref_words_nr) == maxwords) { + sciprintf("Out of reference words\n"); + return; /* return gracefully */ + } + + ref_words[*ref_words_nr] = word; /* register word */ + ++(*ref_words_nr); + + } + if (major != WORD_TYPE_SYNTACTIC_SUGAR && major != WORD_TYPE_BASE && major != WORD_TYPE_REF) + sciprintf("aug_find_words_recursively(): Unknown word type %03x\n", major); + + } else /* Did NOT find a word group: Attempt to recurse */ + aug_find_words_recursively(tree, pos, base_words, base_words_nr, + ref_words, ref_words_nr, maxwords, refbranch || major == WORD_TYPE_REF); + + pos = aug_get_next_sibling(tree, pos, &major, &minor); + } +} + + +static void +aug_find_words(parse_tree_node_t *tree, int startpos, + int *base_words, int *base_words_nr, + int *ref_words, int *ref_words_nr, + int maxwords) + /* initializing wrapper for aug_find_words_recursively() */ +{ + *base_words_nr = 0; + *ref_words_nr = 0; + + aug_find_words_recursively(tree, startpos, base_words, base_words_nr, ref_words, ref_words_nr, maxwords, 0); +} + + +static inline int +aug_contains_word(int *list, int length, int word) +{ + int i; + if (word == ANYWORD) + return (length); + + for (i = 0; i < length; i++) + if (list[i] == word) + return 1; + + return 0; +} + + +static int +augment_sentence_expression(parse_tree_node_t *saidt, int augment_pos, + parse_tree_node_t *parset, int parse_branch, + int major, int minor, + int *base_words, int base_words_nr, + int *ref_words, int ref_words_nr); + +static int +augment_match_expression_p(parse_tree_node_t *saidt, int augment_pos, + parse_tree_node_t *parset, int parse_basepos, + int major, int minor, + int *base_words, int base_words_nr, + int *ref_words, int ref_words_nr) +{ + int cmajor, cminor, cpos; + cpos = aug_get_first_child(saidt, augment_pos, &cmajor, &cminor); + if (!cpos) { + sciprintf("augment_match_expression_p(): Empty condition\n"); + return 1; + } + + scidprintf("Attempting to match (%03x %03x (%03x %03x\n", major, minor, cmajor, cminor); + + if ((major == WORD_TYPE_BASE) && (minor == AUGMENT_SENTENCE_MINOR_RECURSE)) + return augment_match_expression_p(saidt, cpos, + parset, parse_basepos, + cmajor, cminor, + base_words, base_words_nr, + ref_words, ref_words_nr); + + + switch (major) { + + case WORD_TYPE_BASE: + while (cpos) { + if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_WORD) { + int word = aug_get_wgroup(saidt, cpos); + scidprintf("Looking for word %03x\n", word); + + if (aug_contains_word(base_words, base_words_nr, word)) + return 1; + } else if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_PHRASE) { + if (augment_sentence_expression(saidt, cpos, + parset, parse_basepos, + cmajor, cminor, + base_words, base_words_nr, + ref_words, ref_words_nr)) + return 1; + } else if (cminor == AUGMENT_SENTENCE_MINOR_PARENTHESES) { + int gc_major, gc_minor; + int gchild = aug_get_first_child(saidt, cpos, &gc_major, &gc_minor); + + while (gchild) { + if (augment_match_expression_p(saidt, cpos, + parset, parse_basepos, + major, minor, + base_words, base_words_nr, + ref_words, ref_words_nr)) + return 1; + gchild = aug_get_next_sibling(saidt, gchild, &gc_major, &gc_minor); + } + } else + sciprintf("augment_match_expression_p(): Unknown type 141 minor number %3x\n", cminor); + + cpos = aug_get_next_sibling(saidt, cpos, &cmajor, &cminor); + + } + break; + + case WORD_TYPE_REF: + while (cpos) { + if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_WORD) { + int word = aug_get_wgroup(saidt, cpos); + scidprintf("Looking for refword %03x\n", word); + + if (aug_contains_word(ref_words, ref_words_nr, word)) + return 1; + } else if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_PHRASE) { + if (augment_match_expression_p(saidt, cpos, + parset, parse_basepos, + cmajor, cminor, + base_words, base_words_nr, + ref_words, ref_words_nr)) + return 1; + } else if (cminor == AUGMENT_SENTENCE_MINOR_PARENTHESES) { + int gc_major, gc_minor; + int gchild = aug_get_first_child(saidt, cpos, &gc_major, &gc_minor); + + while (gchild) { + if (augment_match_expression_p(saidt, cpos, + parset, parse_basepos, + major, minor, + base_words, base_words_nr, + ref_words, ref_words_nr)) + return 1; + gchild = aug_get_next_sibling(saidt, gchild, &gc_major, &gc_minor); + } + } else + sciprintf("augment_match_expression_p(): Unknown type 144 minor number %3x\n", cminor); + + cpos = aug_get_next_sibling(saidt, cpos, &cmajor, &cminor); + + } + break; + + case AUGMENT_SENTENCE_PART_BRACKETS: + if (augment_match_expression_p(saidt, cpos, + parset, parse_basepos, + cmajor, cminor, + base_words, base_words_nr, + ref_words, ref_words_nr)) + return 1; + + scidprintf("Didn't match subexpression; checking sub-bracked predicate %03x\n", cmajor); + + switch (cmajor) { + case WORD_TYPE_BASE: + if (!base_words_nr) + return 1; + break; + + case WORD_TYPE_REF: + if (!ref_words_nr) + return 1; + break; + + default: + sciprintf("augment_match_expression_p(): (subp1) Unkonwn sub-bracket predicate %03x\n", cmajor); + } + + break; + + default: + sciprintf("augment_match_expression_p(): Unknown predicate %03x\n", major); + + } + + scidprintf("Generic failure\n"); + return 0; +} + +static int +augment_sentence_expression(parse_tree_node_t *saidt, int augment_pos, + parse_tree_node_t *parset, int parse_branch, + int major, int minor, + int *base_words, int base_words_nr, + int *ref_words, int ref_words_nr) +{ + int check_major, check_minor; + int check_pos = aug_get_first_child(saidt, augment_pos, &check_major, &check_minor); + do { + if (!(augment_match_expression_p(saidt, check_pos, parset, parse_branch, + check_major, check_minor, base_words, base_words_nr, + ref_words, ref_words_nr))) + return 0; + } while ((check_pos = aug_get_next_sibling(saidt, check_pos, &check_major, &check_minor))); + return 1; +} + + + +static int +augment_sentence_part(parse_tree_node_t *saidt, int augment_pos, + parse_tree_node_t *parset, int parse_basepos, + int major, int minor) +{ + int pmajor, pminor; + int parse_branch = parse_basepos; + int optional = 0; + int foundwords = 0; + + scidprintf("Augmenting (%03x %03x\n", major, minor); + + if (major == AUGMENT_SENTENCE_PART_BRACKETS) { /* '[/ foo]' is true if '/foo' or if there + ** exists no x for which '/x' is true + */ + if ((augment_pos = aug_get_first_child(saidt, augment_pos, &major, &minor))) { + scidprintf("Optional part: Now augmenting (%03x %03x\n", major, minor); + optional = 1; + } else { + scidprintf("Matched empty optional expression\n"); + return 1; + } + } + + if ((major < 0x141) + || (major > 0x143)) { + scidprintf("augment_sentence_part(): Unexpected sentence part major number %03x\n", major); + return 0; + } + + while ((parse_branch = aug_get_next_sibling(parset, parse_branch, &pmajor, &pminor))) + if (pmajor == major) { /* found matching sentence part */ + int success; + int base_words_nr; + int ref_words_nr; + int base_words[AUGMENT_MAX_WORDS]; + int ref_words[AUGMENT_MAX_WORDS]; +#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION + int i; +#endif + + scidprintf("Found match with pminor = %03x\n", pminor); + aug_find_words(parset, parse_branch, base_words, &base_words_nr, + ref_words, &ref_words_nr, AUGMENT_MAX_WORDS); + foundwords |= (ref_words_nr | base_words_nr); +#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION + sciprintf("%d base words:", base_words_nr); + for (i = 0; i < base_words_nr; i++) + sciprintf(" %03x", base_words[i]); + sciprintf("\n%d reference words:", ref_words_nr); + for (i = 0; i < ref_words_nr; i++) + sciprintf(" %03x", ref_words[i]); + sciprintf("\n"); +#endif + + success = augment_sentence_expression(saidt, augment_pos, + parset, parse_basepos, major, minor, + base_words, base_words_nr, + ref_words, ref_words_nr); + + if (success) { + scidprintf("SUCCESS on augmenting (%03x %03x\n", major, minor); + return 1; + } + } + + if (optional && (foundwords == 0)) { + scidprintf("Found no words and optional branch => SUCCESS on augmenting (%03x %03x\n", major, minor); + return 1; + } + scidprintf("FAILURE on augmenting (%03x %03x\n", major, minor); + return 0; +} + +static int +augment_parse_nodes(parse_tree_node_t *parset, parse_tree_node_t *saidt) +{ + int augment_basepos = 0; + int parse_basepos; + int major, minor; + int dontclaim = 0; + + parse_basepos = aug_get_base_node(parset); + if (!parse_basepos) { + sciprintf("augment_parse_nodes(): Parse tree is corrupt\n"); + return 0; + } + + augment_basepos = aug_get_base_node(saidt); + if (!augment_basepos) { + sciprintf("augment_parse_nodes(): Said tree is corrupt\n"); + return 0; + } + while ((augment_basepos = aug_get_next_sibling(saidt, augment_basepos, &major, &minor))) { + + if ((major == 0x14b) + && (minor == SAID_LONG(SAID_GT))) + dontclaim = 1; /* special case */ + else /* normal sentence part */ + if (!(augment_sentence_part(saidt, augment_basepos, parset, parse_basepos, major, minor))) { + scidprintf("Returning failure\n"); + return 0; /* fail */ + } + } + + scidprintf("Returning success with dontclaim=%d\n", dontclaim); + + if (dontclaim) + return SAID_PARTIAL_MATCH; + else return 1; /* full match */ +} + + +/*******************/ +/**** Main code ****/ +/*******************/ + +int +said(state_t *s, byte *spec, int verbose) +{ + int retval; + + parse_tree_node_t *parse_tree_ptr = s->parser_nodes; + + if (s->parser_valid) { + + if (said_parse_spec(s, spec)) { + sciprintf("Offending spec was: "); + vocab_decypher_said_block(s, spec); + return SAID_NO_MATCH; + } + + if (verbose) + vocab_dump_parse_tree("Said-tree", said_tree); /* Nothing better to do yet */ + retval = augment_parse_nodes(parse_tree_ptr, &(said_tree[0])); + + if (!retval) + return SAID_NO_MATCH; + else if (retval != SAID_PARTIAL_MATCH) + return SAID_FULL_MATCH; + else return SAID_PARTIAL_MATCH; + } + + return SAID_NO_MATCH; +} + + + +#ifdef SAID_DEBUG_PROGRAM +int +main (int argc, char *argv) +{ + byte block[] = {0x01, 0x00, 0xf8, 0xf5, 0x02, 0x01, 0xf6, 0xf2, 0x02, 0x01, 0xf2, 0x01, 0x03, 0xff}; + state_t s; + con_passthrough = 1; + + s.parser_valid = 1; + said(&s, block); +} +#endif diff --git a/engines/sci/engine/savegame.c b/engines/sci/engine/savegame.c new file mode 100644 index 0000000000..0537fcacd5 --- /dev/null +++ b/engines/sci/engine/savegame.c @@ -0,0 +1,4894 @@ +/*************************************************************************** + savegame.cfsml Copyright (C) 1999 Christoph Reichenbach, TU Darmstadt + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ +/* Savegame handling for state_t structs. Makes heavy use of cfsml magic. */ +/* DON'T EDIT savegame.c ! Only modify savegame.cfsml, if something needs +** to be changed. Refer to freesci/docs/misc/cfsml.spec if you don't understand +** savegame.cfsml. If this doesn't solve your problem, contact the maintainer. +*/ + +#include +#include +#include +#include +#include +#include "heap.h" + +#ifdef _MSC_VER +#include +#endif + +#ifdef _WIN32 +#pragma warning( disable : 4101 ) +#endif + +#define HUNK_TYPE_GFX_SNAPSHOT_STRING "g\n" + +/* Missing: +** - SFXdriver +** - File input/output state (this is likely not to happen) +*/ + +static state_t *_global_save_state; +/* Needed for some graphical stuff. */ +#define FILE_VERSION _global_save_state->savegame_version + + +void +write_reg_t(FILE *fh, reg_t *foo) +{ + fprintf(fh, PREG, PRINT_REG(*foo)); +} + +int +read_reg_t(FILE *fh, reg_t *foo, char *lastval, int *line, int *hiteof) +{ + int segment, offset; + + if (sscanf(lastval, PREG, &segment, &offset)<2) + { + sciprintf("Error parsing reg_t on line %d\n", *line); + return 1; + } + + *foo = make_reg(segment, offset); + return 0; +} + +void +write_sci_version(FILE *fh, sci_version_t *foo) +{ + fprintf(fh, "%d.%03d.%03d", SCI_VERSION_MAJOR(*foo), SCI_VERSION_MINOR(*foo), + SCI_VERSION_PATCHLEVEL(*foo)); +} + +int +read_sci_version(FILE *fh, sci_version_t *foo, char *lastval, int *line, int *hiteof) +{ + return version_parse(lastval, foo); +} + +void +write_PTN(FILE *fh, parse_tree_node_t *foo) +{ + if (foo->type == PARSE_TREE_NODE_LEAF) + fprintf(fh, "L%d", foo->content.value); + else + fprintf(fh, "B(%d,%d)", foo->content.branches[0], foo->content.branches[1]); +} + +int +read_PTN(FILE *fh, parse_tree_node_t *foo, char *lastval, int *line, int *hiteof) +{ + if (lastval[0] == 'L') { + char *c = lastval + 1; + char *strend; + + while (*c && isspace(*c)) + ++c; + + if (!*c) + return 1; + + foo->content.value = strtol(c, &strend, 0); + + return (strend == c); /* Error if nothing could be read */ + + return 0; + } else if (lastval[0] == 'B') { + char *c = lastval + 1; + char *strend; + + while (*c && isspace(*c)) ++c; + if (*c++ != '(') return 1; + while (*c && isspace(*c)) ++c; + + foo->content.branches[0] = strtol(c, &strend, 0); + if (strend == c) + return 1; + c = strend; + + while (*c && isspace(*c)) ++c; + if (*c++ != ',') + return 1; + + while (*c && isspace(*c)) ++c; + + foo->content.branches[1] = strtol(c, &strend, 0); + if (strend == c) + return 1; + c = strend; + + while (*c && isspace(*c)) ++c; + if (*c++ != ')') return 1; + + return 0; + } else return 1; /* failure to parse anything */ +} + + +void +write_menubar_tp(FILE *fh, menubar_t **foo); +int +read_menubar_tp(FILE *fh, menubar_t **foo, char *lastval, int *line, int *hiteof); + +void +write_mem_obj_tp(FILE *fh, mem_obj_t **foo); +int +read_mem_obj_tp(FILE *fh, mem_obj_t **foo, char *lastval, int *line, int *hiteof); + +void +write_int_hash_map_tp(FILE *fh, int_hash_map_t **foo); +int +read_int_hash_map_tp(FILE *fh, int_hash_map_t **foo, char *lastval, int *line, int *hiteof); + +void +write_songlib_t(FILE *fh, songlib_t *foo); +int +read_songlib_t(FILE *fh, songlib_t *foo, char *lastval, int *line, int *hiteof); + +void +write_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo); +int +read_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo, char *lastval, int *line, int *hiteof); + +int +read_song_tp(FILE *fh, song_t **foo, char *lastval, int *line, int *hiteof); + +typedef mem_obj_t *mem_obj_ptr; + + +/* Auto-generated CFSML declaration and function block */ + +#define CFSML_SUCCESS 0 +#define CFSML_FAILURE 1 + + +#include /* We need va_lists */ +#include + +#ifdef CFSML_DEBUG_MALLOC +/* +#define free(p) dbg_sci_free(p) +#define malloc(s) dbg_sci_malloc(s) +#define calloc(n, s) dbg_sci_calloc(n, s) +#define realloc(p, s) dbg_sci_realloc(p, s) +*/ +#define free dbg_sci_free +#define malloc dbg_sci_malloc +#define calloc dbg_sci_calloc +#define realloc dbg_sci_realloc +#endif + +static void +_cfsml_error(char *fmt, ...) +{ + va_list argp; + + fprintf(stderr, "Error: "); + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); + +} + + +static struct _cfsml_pointer_refstruct { + struct _cfsml_pointer_refstruct *next; + void *ptr; +} *_cfsml_pointer_references = NULL; + +static struct _cfsml_pointer_refstruct **_cfsml_pointer_references_current = &_cfsml_pointer_references; + +static char *_cfsml_last_value_retreived = NULL; +static char *_cfsml_last_identifier_retreived = NULL; + +static void +_cfsml_free_pointer_references_recursively(struct _cfsml_pointer_refstruct *refs, int free_pointers) +{ + if (!refs) + return; + #ifdef CFSML_DEBUG_MALLOC + SCI_MEMTEST; + #endif + + _cfsml_free_pointer_references_recursively(refs->next, free_pointers); + #ifdef CFSML_DEBUG_MALLOC + SCI_MEMTEST; + + fprintf(stderr,"Freeing ptrref %p [%p] %s\n", refs->ptr, refs, free_pointers? + "ALL": "cleanup only"); + #endif + + if (free_pointers) + free(refs->ptr); + + #ifdef CFSML_DEBUG_MALLOC + SCI_MEMTEST; + #endif + free(refs); + #ifdef CFSML_DEBUG_MALLOC + SCI_MEMTEST; + #endif +} + +static void +_cfsml_free_pointer_references(struct _cfsml_pointer_refstruct **meta_ref, int free_pointers) +{ + _cfsml_free_pointer_references_recursively(*meta_ref, free_pointers); + *meta_ref = NULL; + _cfsml_pointer_references_current = meta_ref; +} + +static struct _cfsml_pointer_refstruct ** +_cfsml_get_current_refpointer() +{ + return _cfsml_pointer_references_current; +} + +static void _cfsml_register_pointer(void *ptr) +{ + struct _cfsml_pointer_refstruct *newref = (struct _cfsml_pointer_refstruct*)sci_malloc(sizeof (struct _cfsml_pointer_refstruct)); + #ifdef CFSML_DEBUG_MALLOC + SCI_MEMTEST; + fprintf(stderr,"Registering ptrref %p [%p]\n", ptr, newref); + #endif + newref->next = *_cfsml_pointer_references_current; + newref->ptr = ptr; + *_cfsml_pointer_references_current = newref; +} + + +static char * +_cfsml_mangle_string(char *s) +{ + char *source = s; + char c; + char *target = (char *) sci_malloc(1 + strlen(s) * 2); /* We will probably need less than that */ + char *writer = target; + + while ((c = *source++)) { + + if (c < 32) { /* Special character? */ + *writer++ = '\\'; /* Escape... */ + c += ('a' - 1); + } else if (c == '\\' || c == '"') + *writer++ = '\\'; /* Escape, but do not change */ + *writer++ = c; + + } + *writer = 0; /* Terminate string */ + + return (char *) sci_realloc(target, strlen(target) + 1); +} + + +static char * +_cfsml_unmangle_string(char *s) +{ + char *target = (char *) sci_malloc(1 + strlen(s)); + char *writer = target; + char *source = s; + char c; + + while ((c = *source++) && (c > 31)) { + if (c == '\\') { /* Escaped character? */ + c = *source++; + if ((c != '\\') && (c != '"')) /* Un-escape 0-31 only */ + c -= ('a' - 1); + } + *writer++ = c; + } + *writer = 0; /* Terminate string */ + + return (char *) sci_realloc(target, strlen(target) + 1); +} + + +static char * +_cfsml_get_identifier(FILE *fd, int *line, int *hiteof, int *assignment) +{ + int c; + int mem = 32; + int pos = 0; + int done = 0; + char *retval = (char *) sci_malloc(mem); + + if (_cfsml_last_identifier_retreived) { + free(_cfsml_last_identifier_retreived); + _cfsml_last_identifier_retreived = NULL; + } + + while (isspace(c = fgetc(fd)) && (c != EOF)); + if (c == EOF) { + _cfsml_error("Unexpected end of file at line %d\n", *line); + free(retval); + *hiteof = 1; + return NULL; + } + + ungetc(c, fd); + + while (((c = fgetc(fd)) != EOF) && ((pos == 0) || (c != '\n')) && (c != '=')) { + + if (pos == mem - 1) /* Need more memory? */ + retval = (char *) sci_realloc(retval, mem *= 2); + + if (!isspace(c)) { + if (done) { + _cfsml_error("Single word identifier expected at line %d\n", *line); + free(retval); + return NULL; + } + retval[pos++] = c; + } else + if (pos != 0) + done = 1; /* Finished the variable name */ + else if (c == '\n') + ++(*line); + } + + if (c == EOF) { + _cfsml_error("Unexpected end of file at line %d\n", *line); + free(retval); + *hiteof = 1; + return NULL; + } + + if (c == '\n') { + ++(*line); + if (assignment) + *assignment = 0; + } else + if (assignment) + *assignment = 1; + + if (pos == 0) { + _cfsml_error("Missing identifier in assignment at line %d\n", *line); + free(retval); + return NULL; + } + + if (pos == mem - 1) /* Need more memory? */ + retval = (char *) sci_realloc(retval, mem += 1); + + retval[pos] = 0; /* Terminate string */ + + return _cfsml_last_identifier_retreived = retval; +} + + +static char * +_cfsml_get_value(FILE *fd, int *line, int *hiteof) +{ + int c; + int mem = 64; + int pos = 0; + char *retval = (char *) sci_malloc(mem); + + if (_cfsml_last_value_retreived) { + free(_cfsml_last_value_retreived); + _cfsml_last_value_retreived = NULL; + } + + while (((c = fgetc(fd)) != EOF) && (c != '\n')) { + + if (pos == mem - 1) /* Need more memory? */ + retval = (char *) sci_realloc(retval, mem *= 2); + + if (pos || (!isspace(c))) + retval[pos++] = c; + + } + + while ((pos > 0) && (isspace(retval[pos - 1]))) + --pos; /* Strip trailing whitespace */ + + if (c == EOF) + *hiteof = 1; + + if (pos == 0) { + _cfsml_error("Missing value in assignment at line %d\n", *line); + free(retval); + return NULL; + } + + if (c == '\n') + ++(*line); + + if (pos == mem - 1) /* Need more memory? */ + retval = (char *) sci_realloc(retval, mem += 1); + + retval[pos] = 0; /* Terminate string */ + return (_cfsml_last_value_retreived = (char *) sci_realloc(retval, strlen(retval) + 1)); + /* Re-allocate; this value might be used for quite some while (if we are + ** restoring a string) + */ +} +static void +_cfsml_write_synonym_t(FILE *fh, synonym_t* save_struc); +static int +_cfsml_read_synonym_t(FILE *fh, synonym_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_sfx_state_t(FILE *fh, sfx_state_t* save_struc); +static int +_cfsml_read_sfx_state_t(FILE *fh, sfx_state_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_clone_entry_t(FILE *fh, clone_entry_t* save_struc); +static int +_cfsml_read_clone_entry_t(FILE *fh, clone_entry_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_object_t(FILE *fh, object_t* save_struc); +static int +_cfsml_read_object_t(FILE *fh, object_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_string(FILE *fh, char ** save_struc); +static int +_cfsml_read_string(FILE *fh, char ** save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_menubar_t(FILE *fh, menubar_t* save_struc); +static int +_cfsml_read_menubar_t(FILE *fh, menubar_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_size_t(FILE *fh, size_t* save_struc); +static int +_cfsml_read_size_t(FILE *fh, size_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_list_entry_t(FILE *fh, list_entry_t* save_struc); +static int +_cfsml_read_list_entry_t(FILE *fh, list_entry_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_int_hash_map_t(FILE *fh, int_hash_map_t* save_struc); +static int +_cfsml_read_int_hash_map_t(FILE *fh, int_hash_map_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_gint16(FILE *fh, gint16* save_struc); +static int +_cfsml_read_gint16(FILE *fh, gint16* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_song_t(FILE *fh, song_t* save_struc); +static int +_cfsml_read_song_t(FILE *fh, song_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_menu_item_t(FILE *fh, menu_item_t* save_struc); +static int +_cfsml_read_menu_item_t(FILE *fh, menu_item_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_node_entry_t(FILE *fh, node_entry_t* save_struc); +static int +_cfsml_read_node_entry_t(FILE *fh, node_entry_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_seg_id_t(FILE *fh, seg_id_t* save_struc); +static int +_cfsml_read_seg_id_t(FILE *fh, seg_id_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_dynmem_t(FILE *fh, dynmem_t* save_struc); +static int +_cfsml_read_dynmem_t(FILE *fh, dynmem_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_local_variables_t(FILE *fh, local_variables_t* save_struc); +static int +_cfsml_read_local_variables_t(FILE *fh, local_variables_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_state_t(FILE *fh, state_t* save_struc); +static int +_cfsml_read_state_t(FILE *fh, state_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_node_table_t(FILE *fh, node_table_t* save_struc); +static int +_cfsml_read_node_table_t(FILE *fh, node_table_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_sys_strings_t(FILE *fh, sys_strings_t* save_struc); +static int +_cfsml_read_sys_strings_t(FILE *fh, sys_strings_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_byte(FILE *fh, byte* save_struc); +static int +_cfsml_read_byte(FILE *fh, byte* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_node_t(FILE *fh, node_t* save_struc); +static int +_cfsml_read_node_t(FILE *fh, node_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_list_table_t(FILE *fh, list_table_t* save_struc); +static int +_cfsml_read_list_table_t(FILE *fh, list_table_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_class_t(FILE *fh, class_t* save_struc); +static int +_cfsml_read_class_t(FILE *fh, class_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_song_handle_t(FILE *fh, song_handle_t* save_struc); +static int +_cfsml_read_song_handle_t(FILE *fh, song_handle_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_int(FILE *fh, int* save_struc); +static int +_cfsml_read_int(FILE *fh, int* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_menu_t(FILE *fh, menu_t* save_struc); +static int +_cfsml_read_menu_t(FILE *fh, menu_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_long(FILE *fh, long* save_struc); +static int +_cfsml_read_long(FILE *fh, long* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_clone_table_t(FILE *fh, clone_table_t* save_struc); +static int +_cfsml_read_clone_table_t(FILE *fh, clone_table_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_clone_t(FILE *fh, clone_t* save_struc); +static int +_cfsml_read_clone_t(FILE *fh, clone_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_list_t(FILE *fh, list_t* save_struc); +static int +_cfsml_read_list_t(FILE *fh, list_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_sys_string_t(FILE *fh, sys_string_t* save_struc); +static int +_cfsml_read_sys_string_t(FILE *fh, sys_string_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_script_t(FILE *fh, script_t* save_struc); +static int +_cfsml_read_script_t(FILE *fh, script_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_seg_manager_t(FILE *fh, seg_manager_t* save_struc); +static int +_cfsml_read_seg_manager_t(FILE *fh, seg_manager_t* save_struc, char *lastval, int *line, int *hiteof); + +static void +_cfsml_write_synonym_t(FILE *fh, synonym_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "replaceant = "); + _cfsml_write_int(fh, (int*) &(save_struc->replaceant)); + fprintf(fh, "\n"); + fprintf(fh, "replacement = "); + _cfsml_write_int(fh, (int*) &(save_struc->replacement)); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_synonym_t(FILE *fh, synonym_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record synonym_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "replaceant")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->replaceant), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for replaceant at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "replacement")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->replacement), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for replacement at line %d\n", *line); + return CFSML_FAILURE; + } + } else + { + _cfsml_error("synonym_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_sfx_state_t(FILE *fh, sfx_state_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "songlib = "); + write_songlib_t(fh, (songlib_t*) &(save_struc->songlib)); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_sfx_state_t(FILE *fh, sfx_state_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record sfx_state_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "songlib")) { + if (read_songlib_t(fh, (songlib_t*) &(save_struc->songlib), value, line, hiteof)) { + _cfsml_error("Token expected by read_songlib_t() for songlib at line %d\n", *line); + return CFSML_FAILURE; + } + } else + { + _cfsml_error("sfx_state_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_clone_entry_t(FILE *fh, clone_entry_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "next_free = "); + _cfsml_write_int(fh, (int*) &(save_struc->next_free)); + fprintf(fh, "\n"); + fprintf(fh, "entry = "); + _cfsml_write_clone_t(fh, (clone_t*) &(save_struc->entry)); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_clone_entry_t(FILE *fh, clone_entry_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record clone_entry_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "next_free")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->next_free), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for next_free at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "entry")) { + if (_cfsml_read_clone_t(fh, (clone_t*) &(save_struc->entry), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_clone_t() for entry at line %d\n", *line); + return CFSML_FAILURE; + } + } else + { + _cfsml_error("clone_entry_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_object_t(FILE *fh, object_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "flags = "); + _cfsml_write_int(fh, (int*) &(save_struc->flags)); + fprintf(fh, "\n"); + fprintf(fh, "pos = "); + write_reg_t(fh, (reg_t*) &(save_struc->pos)); + fprintf(fh, "\n"); + fprintf(fh, "variables_nr = "); + _cfsml_write_int(fh, (int*) &(save_struc->variables_nr)); + fprintf(fh, "\n"); + fprintf(fh, "variable_names_nr = "); + _cfsml_write_int(fh, (int*) &(save_struc->variable_names_nr)); + fprintf(fh, "\n"); + fprintf(fh, "methods_nr = "); + _cfsml_write_int(fh, (int*) &(save_struc->methods_nr)); + fprintf(fh, "\n"); + fprintf(fh, "variables = "); + min = max = save_struc->variables_nr; + if (!save_struc->variables) + min = max = 0; /* Don't write if it points to NULL */ + fprintf(fh, "[%d][\n", max); + for (i = 0; i < min; i++) { + write_reg_t(fh, &(save_struc->variables[i])); + fprintf(fh, "\n"); + } + fprintf(fh, "]"); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_object_t(FILE *fh, object_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record object_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "flags")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->flags), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for flags at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "pos")) { + if (read_reg_t(fh, (reg_t*) &(save_struc->pos), value, line, hiteof)) { + _cfsml_error("Token expected by read_reg_t() for pos at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "variables_nr")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->variables_nr), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for variables_nr at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "variable_names_nr")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->variable_names_nr), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for variable_names_nr at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "methods_nr")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->methods_nr), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for methods_nr at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "variables")) { + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } + /* Prepare to restore dynamic array */ + max = strtol(value + 1, NULL, 0); + if (max < 0) { + _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line); + return CFSML_FAILURE; + } + + if (max) { + save_struc->variables = (reg_t *) sci_malloc(max * sizeof(reg_t)); +#ifdef SATISFY_PURIFY + memset(save_struc->variables, 0, max * sizeof(reg_t)); +#endif + _cfsml_register_pointer(save_struc->variables); + } + else + save_struc->variables = NULL; + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { + _cfsml_error("Token expected at line %d\n", *line); + return 1; + } + if (strcmp(value, "]")) { + if (i == max) { + _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line); + return CFSML_FAILURE; + } + if (read_reg_t(fh, &(save_struc->variables[i++]), value, line, hiteof)) { + _cfsml_error("Token expected by read_reg_t() for variables[i++] at line %d\n", *line); + return CFSML_FAILURE; + } + } else done = 1; + } while (!done); + save_struc->variables_nr = max ; /* Set array size accordingly */ + } else + { + _cfsml_error("object_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_string(FILE *fh, char ** save_struc) +{ + if (!(*save_struc)) + fprintf(fh, "\\null\\"); + else { + char *token = _cfsml_mangle_string((char *) *save_struc); + fprintf(fh, "\"%s\"", token); + free(token); + } +} + +static int +_cfsml_read_string(FILE *fh, char ** save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; + + if (strcmp(lastval, "\\null\\")) { /* null pointer? */ + if (*lastval == '"') { /* Quoted string? */ + int seeker = strlen(lastval); + + while (lastval[seeker] != '"') + --seeker; + + if (!seeker) { /* No matching double-quotes? */ + _cfsml_error("Unbalanced quotes at line %d\n", *line); + return CFSML_FAILURE; + } + + lastval[seeker] = 0; /* Terminate string at closing quotes... */ + lastval++; /* ...and skip the opening quotes locally */ + } + *save_struc = _cfsml_unmangle_string(lastval); + _cfsml_register_pointer(*save_struc); + return CFSML_SUCCESS; + } else { + *save_struc = NULL; + return CFSML_SUCCESS; + } +} + +static void +_cfsml_write_menubar_t(FILE *fh, menubar_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "menus = "); + min = max = save_struc->menus_nr; + if (!save_struc->menus) + min = max = 0; /* Don't write if it points to NULL */ + fprintf(fh, "[%d][\n", max); + for (i = 0; i < min; i++) { + _cfsml_write_menu_t(fh, &(save_struc->menus[i])); + fprintf(fh, "\n"); + } + fprintf(fh, "]"); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_menubar_t(FILE *fh, menubar_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record menubar_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "menus")) { + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } + /* Prepare to restore dynamic array */ + max = strtol(value + 1, NULL, 0); + if (max < 0) { + _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line); + return CFSML_FAILURE; + } + + if (max) { + save_struc->menus = (menu_t *) sci_malloc(max * sizeof(menu_t)); +#ifdef SATISFY_PURIFY + memset(save_struc->menus, 0, max * sizeof(menu_t)); +#endif + _cfsml_register_pointer(save_struc->menus); + } + else + save_struc->menus = NULL; + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { + _cfsml_error("Token expected at line %d\n", *line); + return 1; + } + if (strcmp(value, "]")) { + if (i == max) { + _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line); + return CFSML_FAILURE; + } + if (_cfsml_read_menu_t(fh, &(save_struc->menus[i++]), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_menu_t() for menus[i++] at line %d\n", *line); + return CFSML_FAILURE; + } + } else done = 1; + } while (!done); + save_struc->menus_nr = max ; /* Set array size accordingly */ + } else + { + _cfsml_error("menubar_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_size_t(FILE *fh, size_t* save_struc) +{ + fprintf(fh, "%li", (long) *save_struc); +} + +static int +_cfsml_read_size_t(FILE *fh, size_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; + + *save_struc = strtol(lastval, &token, 0); + if ( (*save_struc == 0) && (token == lastval) ) { + _cfsml_error("strtol failed at line %d\n", *line); + return CFSML_FAILURE; + } + if (*token != 0) { + _cfsml_error("Non-integer encountered while parsing int value at line %d\n", *line); + return CFSML_FAILURE; + } + return CFSML_SUCCESS; +} + +static void +_cfsml_write_list_entry_t(FILE *fh, list_entry_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "next_free = "); + _cfsml_write_int(fh, (int*) &(save_struc->next_free)); + fprintf(fh, "\n"); + fprintf(fh, "entry = "); + _cfsml_write_list_t(fh, (list_t*) &(save_struc->entry)); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_list_entry_t(FILE *fh, list_entry_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record list_entry_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "next_free")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->next_free), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for next_free at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "entry")) { + if (_cfsml_read_list_t(fh, (list_t*) &(save_struc->entry), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_list_t() for entry at line %d\n", *line); + return CFSML_FAILURE; + } + } else + { + _cfsml_error("list_entry_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_int_hash_map_t(FILE *fh, int_hash_map_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "base_value = "); + _cfsml_write_int(fh, (int*) &(save_struc->base_value)); + fprintf(fh, "\n"); + fprintf(fh, "nodes = "); + min = max = DCS_INT_HASH_MAX+1; + fprintf(fh, "[%d][\n", max); + for (i = 0; i < min; i++) { + write_int_hash_map_node_tp(fh, &(save_struc->nodes[i])); + fprintf(fh, "\n"); + } + fprintf(fh, "]"); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_int_hash_map_t(FILE *fh, int_hash_map_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record int_hash_map_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "base_value")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->base_value), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for base_value at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "nodes")) { + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } + /* Prepare to restore static array */ + max = DCS_INT_HASH_MAX+1; + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { + _cfsml_error("Token expected at line %d\n", *line); + return 1; + } + if (strcmp(value, "]")) { + if (i == max) { + _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line); + return CFSML_FAILURE; + } + if (read_int_hash_map_node_tp(fh, &(save_struc->nodes[i++]), value, line, hiteof)) { + _cfsml_error("Token expected by read_int_hash_map_node_tp() for nodes[i++] at line %d\n", *line); + return CFSML_FAILURE; + } + } else done = 1; + } while (!done); + } else + { + _cfsml_error("int_hash_map_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_gint16(FILE *fh, gint16* save_struc) +{ + fprintf(fh, "%li", (long) *save_struc); +} + +static int +_cfsml_read_gint16(FILE *fh, gint16* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; + + *save_struc = strtol(lastval, &token, 0); + if ( (*save_struc == 0) && (token == lastval) ) { + _cfsml_error("strtol failed at line %d\n", *line); + return CFSML_FAILURE; + } + if (*token != 0) { + _cfsml_error("Non-integer encountered while parsing int value at line %d\n", *line); + return CFSML_FAILURE; + } + return CFSML_SUCCESS; +} + +static void +_cfsml_write_song_t(FILE *fh, song_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "handle = "); + _cfsml_write_song_handle_t(fh, (song_handle_t*) &(save_struc->handle)); + fprintf(fh, "\n"); + fprintf(fh, "resource_num = "); + _cfsml_write_int(fh, (int*) &(save_struc->resource_num)); + fprintf(fh, "\n"); + fprintf(fh, "priority = "); + _cfsml_write_int(fh, (int*) &(save_struc->priority)); + fprintf(fh, "\n"); + fprintf(fh, "status = "); + _cfsml_write_int(fh, (int*) &(save_struc->status)); + fprintf(fh, "\n"); + fprintf(fh, "restore_behavior = "); + _cfsml_write_int(fh, (int*) &(save_struc->restore_behavior)); + fprintf(fh, "\n"); + fprintf(fh, "restore_time = "); + _cfsml_write_int(fh, (int*) &(save_struc->restore_time)); + fprintf(fh, "\n"); + fprintf(fh, "loops = "); + _cfsml_write_int(fh, (int*) &(save_struc->loops)); + fprintf(fh, "\n"); + fprintf(fh, "hold = "); + _cfsml_write_int(fh, (int*) &(save_struc->hold)); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_song_t(FILE *fh, song_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record song_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "handle")) { + if (_cfsml_read_song_handle_t(fh, (song_handle_t*) &(save_struc->handle), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_song_handle_t() for handle at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "resource_num")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->resource_num), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for resource_num at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "priority")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->priority), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for priority at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "status")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->status), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for status at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "restore_behavior")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->restore_behavior), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for restore_behavior at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "restore_time")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->restore_time), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for restore_time at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "loops")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->loops), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for loops at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "hold")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->hold), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for hold at line %d\n", *line); + return CFSML_FAILURE; + } + } else + { + _cfsml_error("song_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_menu_item_t(FILE *fh, menu_item_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "type = "); + _cfsml_write_int(fh, (int*) &(save_struc->type)); + fprintf(fh, "\n"); + fprintf(fh, "keytext = "); + _cfsml_write_string(fh, (char **) &(save_struc->keytext)); + fprintf(fh, "\n"); + fprintf(fh, "keytext_size = "); + _cfsml_write_int(fh, (int*) &(save_struc->keytext_size)); + fprintf(fh, "\n"); + fprintf(fh, "flags = "); + _cfsml_write_int(fh, (int*) &(save_struc->flags)); + fprintf(fh, "\n"); + fprintf(fh, "said = "); + min = max = MENU_SAID_SPEC_SIZE; + fprintf(fh, "[%d][\n", max); + for (i = 0; i < min; i++) { + _cfsml_write_byte(fh, &(save_struc->said[i])); + fprintf(fh, "\n"); + } + fprintf(fh, "]"); + fprintf(fh, "\n"); + fprintf(fh, "said_pos = "); + write_reg_t(fh, (reg_t*) &(save_struc->said_pos)); + fprintf(fh, "\n"); + fprintf(fh, "text = "); + _cfsml_write_string(fh, (char **) &(save_struc->text)); + fprintf(fh, "\n"); + fprintf(fh, "text_pos = "); + write_reg_t(fh, (reg_t*) &(save_struc->text_pos)); + fprintf(fh, "\n"); + fprintf(fh, "modifiers = "); + _cfsml_write_int(fh, (int*) &(save_struc->modifiers)); + fprintf(fh, "\n"); + fprintf(fh, "key = "); + _cfsml_write_int(fh, (int*) &(save_struc->key)); + fprintf(fh, "\n"); + fprintf(fh, "enabled = "); + _cfsml_write_int(fh, (int*) &(save_struc->enabled)); + fprintf(fh, "\n"); + fprintf(fh, "tag = "); + _cfsml_write_int(fh, (int*) &(save_struc->tag)); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_menu_item_t(FILE *fh, menu_item_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record menu_item_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "type")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->type), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for type at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "keytext")) { + if (_cfsml_read_string(fh, (char **) &(save_struc->keytext), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_string() for keytext at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "keytext_size")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->keytext_size), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for keytext_size at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "flags")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->flags), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for flags at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "said")) { + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } + /* Prepare to restore static array */ + max = MENU_SAID_SPEC_SIZE; + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { + _cfsml_error("Token expected at line %d\n", *line); + return 1; + } + if (strcmp(value, "]")) { + if (i == max) { + _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line); + return CFSML_FAILURE; + } + if (_cfsml_read_byte(fh, &(save_struc->said[i++]), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_byte() for said[i++] at line %d\n", *line); + return CFSML_FAILURE; + } + } else done = 1; + } while (!done); + } else + if (!strcmp(token, "said_pos")) { + if (read_reg_t(fh, (reg_t*) &(save_struc->said_pos), value, line, hiteof)) { + _cfsml_error("Token expected by read_reg_t() for said_pos at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "text")) { + if (_cfsml_read_string(fh, (char **) &(save_struc->text), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_string() for text at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "text_pos")) { + if (read_reg_t(fh, (reg_t*) &(save_struc->text_pos), value, line, hiteof)) { + _cfsml_error("Token expected by read_reg_t() for text_pos at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "modifiers")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->modifiers), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for modifiers at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "key")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->key), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for key at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "enabled")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->enabled), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for enabled at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "tag")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->tag), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for tag at line %d\n", *line); + return CFSML_FAILURE; + } + } else + { + _cfsml_error("menu_item_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_node_entry_t(FILE *fh, node_entry_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "next_free = "); + _cfsml_write_int(fh, (int*) &(save_struc->next_free)); + fprintf(fh, "\n"); + fprintf(fh, "entry = "); + _cfsml_write_node_t(fh, (node_t*) &(save_struc->entry)); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_node_entry_t(FILE *fh, node_entry_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record node_entry_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "next_free")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->next_free), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for next_free at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "entry")) { + if (_cfsml_read_node_t(fh, (node_t*) &(save_struc->entry), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_node_t() for entry at line %d\n", *line); + return CFSML_FAILURE; + } + } else + { + _cfsml_error("node_entry_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_seg_id_t(FILE *fh, seg_id_t* save_struc) +{ + fprintf(fh, "%li", (long) *save_struc); +} + +static int +_cfsml_read_seg_id_t(FILE *fh, seg_id_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; + + *save_struc = strtol(lastval, &token, 0); + if ( (*save_struc == 0) && (token == lastval) ) { + _cfsml_error("strtol failed at line %d\n", *line); + return CFSML_FAILURE; + } + if (*token != 0) { + _cfsml_error("Non-integer encountered while parsing int value at line %d\n", *line); + return CFSML_FAILURE; + } + return CFSML_SUCCESS; +} + +static void +_cfsml_write_dynmem_t(FILE *fh, dynmem_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "size = "); + _cfsml_write_int(fh, (int*) &(save_struc->size)); + fprintf(fh, "\n"); + fprintf(fh, "description = "); + _cfsml_write_string(fh, (char **) &(save_struc->description)); + fprintf(fh, "\n"); + fprintf(fh, "buf = "); + min = max = save_struc->size; + if (!save_struc->buf) + min = max = 0; /* Don't write if it points to NULL */ + fprintf(fh, "[%d][\n", max); + for (i = 0; i < min; i++) { + _cfsml_write_byte(fh, &(save_struc->buf[i])); + fprintf(fh, "\n"); + } + fprintf(fh, "]"); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_dynmem_t(FILE *fh, dynmem_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record dynmem_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "size")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->size), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for size at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "description")) { + if (_cfsml_read_string(fh, (char **) &(save_struc->description), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_string() for description at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "buf")) { + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } + /* Prepare to restore dynamic array */ + max = strtol(value + 1, NULL, 0); + if (max < 0) { + _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line); + return CFSML_FAILURE; + } + + if (max) { + save_struc->buf = (byte *) sci_malloc(max * sizeof(byte)); +#ifdef SATISFY_PURIFY + memset(save_struc->buf, 0, max * sizeof(byte)); +#endif + _cfsml_register_pointer(save_struc->buf); + } + else + save_struc->buf = NULL; + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { + _cfsml_error("Token expected at line %d\n", *line); + return 1; + } + if (strcmp(value, "]")) { + if (i == max) { + _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line); + return CFSML_FAILURE; + } + if (_cfsml_read_byte(fh, &(save_struc->buf[i++]), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_byte() for buf[i++] at line %d\n", *line); + return CFSML_FAILURE; + } + } else done = 1; + } while (!done); + save_struc->size = max ; /* Set array size accordingly */ + } else + { + _cfsml_error("dynmem_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_local_variables_t(FILE *fh, local_variables_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "script_id = "); + _cfsml_write_int(fh, (int*) &(save_struc->script_id)); + fprintf(fh, "\n"); + fprintf(fh, "nr = "); + _cfsml_write_int(fh, (int*) &(save_struc->nr)); + fprintf(fh, "\n"); + fprintf(fh, "locals = "); + min = max = save_struc->nr; + if (!save_struc->locals) + min = max = 0; /* Don't write if it points to NULL */ + fprintf(fh, "[%d][\n", max); + for (i = 0; i < min; i++) { + write_reg_t(fh, &(save_struc->locals[i])); + fprintf(fh, "\n"); + } + fprintf(fh, "]"); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_local_variables_t(FILE *fh, local_variables_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record local_variables_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "script_id")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->script_id), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for script_id at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "nr")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->nr), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for nr at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "locals")) { + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } + /* Prepare to restore dynamic array */ + max = strtol(value + 1, NULL, 0); + if (max < 0) { + _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line); + return CFSML_FAILURE; + } + + if (max) { + save_struc->locals = (reg_t *) sci_malloc(max * sizeof(reg_t)); +#ifdef SATISFY_PURIFY + memset(save_struc->locals, 0, max * sizeof(reg_t)); +#endif + _cfsml_register_pointer(save_struc->locals); + } + else + save_struc->locals = NULL; + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { + _cfsml_error("Token expected at line %d\n", *line); + return 1; + } + if (strcmp(value, "]")) { + if (i == max) { + _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line); + return CFSML_FAILURE; + } + if (read_reg_t(fh, &(save_struc->locals[i++]), value, line, hiteof)) { + _cfsml_error("Token expected by read_reg_t() for locals[i++] at line %d\n", *line); + return CFSML_FAILURE; + } + } else done = 1; + } while (!done); + save_struc->nr = max ; /* Set array size accordingly */ + } else + { + _cfsml_error("local_variables_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_state_t(FILE *fh, state_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "savegame_version = "); + _cfsml_write_int(fh, (int*) &(save_struc->savegame_version)); + fprintf(fh, "\n"); + fprintf(fh, "game_version = "); + _cfsml_write_string(fh, (char **) &(save_struc->game_version)); + fprintf(fh, "\n"); + fprintf(fh, "version = "); + write_sci_version(fh, (sci_version_t*) &(save_struc->version)); + fprintf(fh, "\n"); + fprintf(fh, "menubar = "); + write_menubar_tp(fh, (menubar_t **) &(save_struc->menubar)); + fprintf(fh, "\n"); + fprintf(fh, "status_bar_foreground = "); + _cfsml_write_int(fh, (int*) &(save_struc->status_bar_foreground)); + fprintf(fh, "\n"); + fprintf(fh, "status_bar_background = "); + _cfsml_write_int(fh, (int*) &(save_struc->status_bar_background)); + fprintf(fh, "\n"); + fprintf(fh, "seg_manager = "); + _cfsml_write_seg_manager_t(fh, (seg_manager_t*) &(save_struc->seg_manager)); + fprintf(fh, "\n"); + fprintf(fh, "classtable_size = "); + _cfsml_write_int(fh, (int*) &(save_struc->classtable_size)); + fprintf(fh, "\n"); + fprintf(fh, "classtable = "); + min = max = save_struc->classtable_size; + if (!save_struc->classtable) + min = max = 0; /* Don't write if it points to NULL */ + fprintf(fh, "[%d][\n", max); + for (i = 0; i < min; i++) { + _cfsml_write_class_t(fh, &(save_struc->classtable[i])); + fprintf(fh, "\n"); + } + fprintf(fh, "]"); + fprintf(fh, "\n"); + fprintf(fh, "sound = "); + _cfsml_write_sfx_state_t(fh, (sfx_state_t*) &(save_struc->sound)); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_state_t(FILE *fh, state_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record state_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "savegame_version")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->savegame_version), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for savegame_version at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "game_version")) { + if (_cfsml_read_string(fh, (char **) &(save_struc->game_version), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_string() for game_version at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "version")) { + if (read_sci_version(fh, (sci_version_t*) &(save_struc->version), value, line, hiteof)) { + _cfsml_error("Token expected by read_sci_version() for version at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "menubar")) { + if (read_menubar_tp(fh, (menubar_t **) &(save_struc->menubar), value, line, hiteof)) { + _cfsml_error("Token expected by read_menubar_tp() for menubar at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "status_bar_foreground")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->status_bar_foreground), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for status_bar_foreground at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "status_bar_background")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->status_bar_background), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for status_bar_background at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "seg_manager")) { + if (_cfsml_read_seg_manager_t(fh, (seg_manager_t*) &(save_struc->seg_manager), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_seg_manager_t() for seg_manager at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "classtable_size")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->classtable_size), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for classtable_size at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "classtable")) { + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } + /* Prepare to restore dynamic array */ + max = strtol(value + 1, NULL, 0); + if (max < 0) { + _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line); + return CFSML_FAILURE; + } + + if (max) { + save_struc->classtable = (class_t *) sci_malloc(max * sizeof(class_t)); +#ifdef SATISFY_PURIFY + memset(save_struc->classtable, 0, max * sizeof(class_t)); +#endif + _cfsml_register_pointer(save_struc->classtable); + } + else + save_struc->classtable = NULL; + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { + _cfsml_error("Token expected at line %d\n", *line); + return 1; + } + if (strcmp(value, "]")) { + if (i == max) { + _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line); + return CFSML_FAILURE; + } + if (_cfsml_read_class_t(fh, &(save_struc->classtable[i++]), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_class_t() for classtable[i++] at line %d\n", *line); + return CFSML_FAILURE; + } + } else done = 1; + } while (!done); + save_struc->classtable_size = max ; /* Set array size accordingly */ + } else + if (!strcmp(token, "sound")) { + if (_cfsml_read_sfx_state_t(fh, (sfx_state_t*) &(save_struc->sound), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_sfx_state_t() for sound at line %d\n", *line); + return CFSML_FAILURE; + } + } else + { + _cfsml_error("state_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_node_table_t(FILE *fh, node_table_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "entries_nr = "); + _cfsml_write_int(fh, (int*) &(save_struc->entries_nr)); + fprintf(fh, "\n"); + fprintf(fh, "first_free = "); + _cfsml_write_int(fh, (int*) &(save_struc->first_free)); + fprintf(fh, "\n"); + fprintf(fh, "entries_used = "); + _cfsml_write_int(fh, (int*) &(save_struc->entries_used)); + fprintf(fh, "\n"); + fprintf(fh, "max_entry = "); + _cfsml_write_int(fh, (int*) &(save_struc->max_entry)); + fprintf(fh, "\n"); + fprintf(fh, "table = "); + min = max = save_struc->entries_nr; + if (!save_struc->table) + min = max = 0; /* Don't write if it points to NULL */ + fprintf(fh, "[%d][\n", max); + for (i = 0; i < min; i++) { + _cfsml_write_node_entry_t(fh, &(save_struc->table[i])); + fprintf(fh, "\n"); + } + fprintf(fh, "]"); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_node_table_t(FILE *fh, node_table_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record node_table_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "entries_nr")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->entries_nr), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for entries_nr at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "first_free")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->first_free), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for first_free at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "entries_used")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->entries_used), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for entries_used at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "max_entry")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->max_entry), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for max_entry at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "table")) { + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } + /* Prepare to restore dynamic array */ + max = strtol(value + 1, NULL, 0); + if (max < 0) { + _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line); + return CFSML_FAILURE; + } + + if (max) { + save_struc->table = (node_entry_t *) sci_malloc(max * sizeof(node_entry_t)); +#ifdef SATISFY_PURIFY + memset(save_struc->table, 0, max * sizeof(node_entry_t)); +#endif + _cfsml_register_pointer(save_struc->table); + } + else + save_struc->table = NULL; + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { + _cfsml_error("Token expected at line %d\n", *line); + return 1; + } + if (strcmp(value, "]")) { + if (i == max) { + _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line); + return CFSML_FAILURE; + } + if (_cfsml_read_node_entry_t(fh, &(save_struc->table[i++]), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_node_entry_t() for table[i++] at line %d\n", *line); + return CFSML_FAILURE; + } + } else done = 1; + } while (!done); + save_struc->entries_nr = max ; /* Set array size accordingly */ + } else + { + _cfsml_error("node_table_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_sys_strings_t(FILE *fh, sys_strings_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "strings = "); + min = max = SYS_STRINGS_MAX; + fprintf(fh, "[%d][\n", max); + for (i = 0; i < min; i++) { + _cfsml_write_sys_string_t(fh, &(save_struc->strings[i])); + fprintf(fh, "\n"); + } + fprintf(fh, "]"); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_sys_strings_t(FILE *fh, sys_strings_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record sys_strings_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "strings")) { + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } + /* Prepare to restore static array */ + max = SYS_STRINGS_MAX; + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { + _cfsml_error("Token expected at line %d\n", *line); + return 1; + } + if (strcmp(value, "]")) { + if (i == max) { + _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line); + return CFSML_FAILURE; + } + if (_cfsml_read_sys_string_t(fh, &(save_struc->strings[i++]), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_sys_string_t() for strings[i++] at line %d\n", *line); + return CFSML_FAILURE; + } + } else done = 1; + } while (!done); + } else + { + _cfsml_error("sys_strings_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_byte(FILE *fh, byte* save_struc) +{ + fprintf(fh, "%li", (long) *save_struc); +} + +static int +_cfsml_read_byte(FILE *fh, byte* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; + + *save_struc = strtol(lastval, &token, 0); + if ( (*save_struc == 0) && (token == lastval) ) { + _cfsml_error("strtol failed at line %d\n", *line); + return CFSML_FAILURE; + } + if (*token != 0) { + _cfsml_error("Non-integer encountered while parsing int value at line %d\n", *line); + return CFSML_FAILURE; + } + return CFSML_SUCCESS; +} + +static void +_cfsml_write_node_t(FILE *fh, node_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "pred = "); + write_reg_t(fh, (reg_t*) &(save_struc->pred)); + fprintf(fh, "\n"); + fprintf(fh, "succ = "); + write_reg_t(fh, (reg_t*) &(save_struc->succ)); + fprintf(fh, "\n"); + fprintf(fh, "key = "); + write_reg_t(fh, (reg_t*) &(save_struc->key)); + fprintf(fh, "\n"); + fprintf(fh, "value = "); + write_reg_t(fh, (reg_t*) &(save_struc->value)); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_node_t(FILE *fh, node_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record node_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "pred")) { + if (read_reg_t(fh, (reg_t*) &(save_struc->pred), value, line, hiteof)) { + _cfsml_error("Token expected by read_reg_t() for pred at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "succ")) { + if (read_reg_t(fh, (reg_t*) &(save_struc->succ), value, line, hiteof)) { + _cfsml_error("Token expected by read_reg_t() for succ at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "key")) { + if (read_reg_t(fh, (reg_t*) &(save_struc->key), value, line, hiteof)) { + _cfsml_error("Token expected by read_reg_t() for key at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "value")) { + if (read_reg_t(fh, (reg_t*) &(save_struc->value), value, line, hiteof)) { + _cfsml_error("Token expected by read_reg_t() for value at line %d\n", *line); + return CFSML_FAILURE; + } + } else + { + _cfsml_error("node_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_list_table_t(FILE *fh, list_table_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "entries_nr = "); + _cfsml_write_int(fh, (int*) &(save_struc->entries_nr)); + fprintf(fh, "\n"); + fprintf(fh, "first_free = "); + _cfsml_write_int(fh, (int*) &(save_struc->first_free)); + fprintf(fh, "\n"); + fprintf(fh, "entries_used = "); + _cfsml_write_int(fh, (int*) &(save_struc->entries_used)); + fprintf(fh, "\n"); + fprintf(fh, "max_entry = "); + _cfsml_write_int(fh, (int*) &(save_struc->max_entry)); + fprintf(fh, "\n"); + fprintf(fh, "table = "); + min = max = save_struc->entries_nr; + if (!save_struc->table) + min = max = 0; /* Don't write if it points to NULL */ + fprintf(fh, "[%d][\n", max); + for (i = 0; i < min; i++) { + _cfsml_write_list_entry_t(fh, &(save_struc->table[i])); + fprintf(fh, "\n"); + } + fprintf(fh, "]"); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_list_table_t(FILE *fh, list_table_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record list_table_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "entries_nr")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->entries_nr), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for entries_nr at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "first_free")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->first_free), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for first_free at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "entries_used")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->entries_used), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for entries_used at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "max_entry")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->max_entry), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for max_entry at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "table")) { + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } + /* Prepare to restore dynamic array */ + max = strtol(value + 1, NULL, 0); + if (max < 0) { + _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line); + return CFSML_FAILURE; + } + + if (max) { + save_struc->table = (list_entry_t *) sci_malloc(max * sizeof(list_entry_t)); +#ifdef SATISFY_PURIFY + memset(save_struc->table, 0, max * sizeof(list_entry_t)); +#endif + _cfsml_register_pointer(save_struc->table); + } + else + save_struc->table = NULL; + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { + _cfsml_error("Token expected at line %d\n", *line); + return 1; + } + if (strcmp(value, "]")) { + if (i == max) { + _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line); + return CFSML_FAILURE; + } + if (_cfsml_read_list_entry_t(fh, &(save_struc->table[i++]), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_list_entry_t() for table[i++] at line %d\n", *line); + return CFSML_FAILURE; + } + } else done = 1; + } while (!done); + save_struc->entries_nr = max ; /* Set array size accordingly */ + } else + { + _cfsml_error("list_table_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_class_t(FILE *fh, class_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "script = "); + _cfsml_write_int(fh, (int*) &(save_struc->script)); + fprintf(fh, "\n"); + fprintf(fh, "reg = "); + write_reg_t(fh, (reg_t*) &(save_struc->reg)); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_class_t(FILE *fh, class_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record class_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "script")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->script), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for script at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "reg")) { + if (read_reg_t(fh, (reg_t*) &(save_struc->reg), value, line, hiteof)) { + _cfsml_error("Token expected by read_reg_t() for reg at line %d\n", *line); + return CFSML_FAILURE; + } + } else + { + _cfsml_error("class_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_song_handle_t(FILE *fh, song_handle_t* save_struc) +{ + fprintf(fh, "%li", (long) *save_struc); +} + +static int +_cfsml_read_song_handle_t(FILE *fh, song_handle_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; + + *save_struc = strtol(lastval, &token, 0); + if ( (*save_struc == 0) && (token == lastval) ) { + _cfsml_error("strtol failed at line %d\n", *line); + return CFSML_FAILURE; + } + if (*token != 0) { + _cfsml_error("Non-integer encountered while parsing int value at line %d\n", *line); + return CFSML_FAILURE; + } + return CFSML_SUCCESS; +} + +static void +_cfsml_write_int(FILE *fh, int* save_struc) +{ + fprintf(fh, "%li", (long) *save_struc); +} + +static int +_cfsml_read_int(FILE *fh, int* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; + + *save_struc = strtol(lastval, &token, 0); + if ( (*save_struc == 0) && (token == lastval) ) { + _cfsml_error("strtol failed at line %d\n", *line); + return CFSML_FAILURE; + } + if (*token != 0) { + _cfsml_error("Non-integer encountered while parsing int value at line %d\n", *line); + return CFSML_FAILURE; + } + return CFSML_SUCCESS; +} + +static void +_cfsml_write_menu_t(FILE *fh, menu_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "title = "); + _cfsml_write_string(fh, (char **) &(save_struc->title)); + fprintf(fh, "\n"); + fprintf(fh, "title_width = "); + _cfsml_write_int(fh, (int*) &(save_struc->title_width)); + fprintf(fh, "\n"); + fprintf(fh, "width = "); + _cfsml_write_int(fh, (int*) &(save_struc->width)); + fprintf(fh, "\n"); + fprintf(fh, "items = "); + min = max = save_struc->items_nr; + if (!save_struc->items) + min = max = 0; /* Don't write if it points to NULL */ + fprintf(fh, "[%d][\n", max); + for (i = 0; i < min; i++) { + _cfsml_write_menu_item_t(fh, &(save_struc->items[i])); + fprintf(fh, "\n"); + } + fprintf(fh, "]"); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_menu_t(FILE *fh, menu_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record menu_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "title")) { + if (_cfsml_read_string(fh, (char **) &(save_struc->title), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_string() for title at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "title_width")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->title_width), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for title_width at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "width")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->width), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for width at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "items")) { + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } + /* Prepare to restore dynamic array */ + max = strtol(value + 1, NULL, 0); + if (max < 0) { + _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line); + return CFSML_FAILURE; + } + + if (max) { + save_struc->items = (menu_item_t *) sci_malloc(max * sizeof(menu_item_t)); +#ifdef SATISFY_PURIFY + memset(save_struc->items, 0, max * sizeof(menu_item_t)); +#endif + _cfsml_register_pointer(save_struc->items); + } + else + save_struc->items = NULL; + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { + _cfsml_error("Token expected at line %d\n", *line); + return 1; + } + if (strcmp(value, "]")) { + if (i == max) { + _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line); + return CFSML_FAILURE; + } + if (_cfsml_read_menu_item_t(fh, &(save_struc->items[i++]), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_menu_item_t() for items[i++] at line %d\n", *line); + return CFSML_FAILURE; + } + } else done = 1; + } while (!done); + save_struc->items_nr = max ; /* Set array size accordingly */ + } else + { + _cfsml_error("menu_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_long(FILE *fh, long* save_struc) +{ + fprintf(fh, "%li", (long) *save_struc); +} + +static int +_cfsml_read_long(FILE *fh, long* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; + + *save_struc = strtol(lastval, &token, 0); + if ( (*save_struc == 0) && (token == lastval) ) { + _cfsml_error("strtol failed at line %d\n", *line); + return CFSML_FAILURE; + } + if (*token != 0) { + _cfsml_error("Non-integer encountered while parsing int value at line %d\n", *line); + return CFSML_FAILURE; + } + return CFSML_SUCCESS; +} + +static void +_cfsml_write_clone_table_t(FILE *fh, clone_table_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "entries_nr = "); + _cfsml_write_int(fh, (int*) &(save_struc->entries_nr)); + fprintf(fh, "\n"); + fprintf(fh, "first_free = "); + _cfsml_write_int(fh, (int*) &(save_struc->first_free)); + fprintf(fh, "\n"); + fprintf(fh, "entries_used = "); + _cfsml_write_int(fh, (int*) &(save_struc->entries_used)); + fprintf(fh, "\n"); + fprintf(fh, "max_entry = "); + _cfsml_write_int(fh, (int*) &(save_struc->max_entry)); + fprintf(fh, "\n"); + fprintf(fh, "table = "); + min = max = save_struc->entries_nr; + if (!save_struc->table) + min = max = 0; /* Don't write if it points to NULL */ + fprintf(fh, "[%d][\n", max); + for (i = 0; i < min; i++) { + _cfsml_write_clone_entry_t(fh, &(save_struc->table[i])); + fprintf(fh, "\n"); + } + fprintf(fh, "]"); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_clone_table_t(FILE *fh, clone_table_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record clone_table_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "entries_nr")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->entries_nr), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for entries_nr at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "first_free")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->first_free), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for first_free at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "entries_used")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->entries_used), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for entries_used at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "max_entry")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->max_entry), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for max_entry at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "table")) { + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } + /* Prepare to restore dynamic array */ + max = strtol(value + 1, NULL, 0); + if (max < 0) { + _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line); + return CFSML_FAILURE; + } + + if (max) { + save_struc->table = (clone_entry_t *) sci_malloc(max * sizeof(clone_entry_t)); +#ifdef SATISFY_PURIFY + memset(save_struc->table, 0, max * sizeof(clone_entry_t)); +#endif + _cfsml_register_pointer(save_struc->table); + } + else + save_struc->table = NULL; + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { + _cfsml_error("Token expected at line %d\n", *line); + return 1; + } + if (strcmp(value, "]")) { + if (i == max) { + _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line); + return CFSML_FAILURE; + } + if (_cfsml_read_clone_entry_t(fh, &(save_struc->table[i++]), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_clone_entry_t() for table[i++] at line %d\n", *line); + return CFSML_FAILURE; + } + } else done = 1; + } while (!done); + save_struc->entries_nr = max ; /* Set array size accordingly */ + } else + { + _cfsml_error("clone_table_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_clone_t(FILE *fh, clone_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "flags = "); + _cfsml_write_int(fh, (int*) &(save_struc->flags)); + fprintf(fh, "\n"); + fprintf(fh, "pos = "); + write_reg_t(fh, (reg_t*) &(save_struc->pos)); + fprintf(fh, "\n"); + fprintf(fh, "variables_nr = "); + _cfsml_write_int(fh, (int*) &(save_struc->variables_nr)); + fprintf(fh, "\n"); + fprintf(fh, "variable_names_nr = "); + _cfsml_write_int(fh, (int*) &(save_struc->variable_names_nr)); + fprintf(fh, "\n"); + fprintf(fh, "methods_nr = "); + _cfsml_write_int(fh, (int*) &(save_struc->methods_nr)); + fprintf(fh, "\n"); + fprintf(fh, "variables = "); + min = max = save_struc->variables_nr; + if (!save_struc->variables) + min = max = 0; /* Don't write if it points to NULL */ + fprintf(fh, "[%d][\n", max); + for (i = 0; i < min; i++) { + write_reg_t(fh, &(save_struc->variables[i])); + fprintf(fh, "\n"); + } + fprintf(fh, "]"); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_clone_t(FILE *fh, clone_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record clone_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "flags")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->flags), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for flags at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "pos")) { + if (read_reg_t(fh, (reg_t*) &(save_struc->pos), value, line, hiteof)) { + _cfsml_error("Token expected by read_reg_t() for pos at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "variables_nr")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->variables_nr), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for variables_nr at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "variable_names_nr")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->variable_names_nr), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for variable_names_nr at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "methods_nr")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->methods_nr), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for methods_nr at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "variables")) { + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } + /* Prepare to restore dynamic array */ + max = strtol(value + 1, NULL, 0); + if (max < 0) { + _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line); + return CFSML_FAILURE; + } + + if (max) { + save_struc->variables = (reg_t *) sci_malloc(max * sizeof(reg_t)); +#ifdef SATISFY_PURIFY + memset(save_struc->variables, 0, max * sizeof(reg_t)); +#endif + _cfsml_register_pointer(save_struc->variables); + } + else + save_struc->variables = NULL; + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { + _cfsml_error("Token expected at line %d\n", *line); + return 1; + } + if (strcmp(value, "]")) { + if (i == max) { + _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line); + return CFSML_FAILURE; + } + if (read_reg_t(fh, &(save_struc->variables[i++]), value, line, hiteof)) { + _cfsml_error("Token expected by read_reg_t() for variables[i++] at line %d\n", *line); + return CFSML_FAILURE; + } + } else done = 1; + } while (!done); + save_struc->variables_nr = max ; /* Set array size accordingly */ + } else + { + _cfsml_error("clone_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_list_t(FILE *fh, list_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "first = "); + write_reg_t(fh, (reg_t*) &(save_struc->first)); + fprintf(fh, "\n"); + fprintf(fh, "last = "); + write_reg_t(fh, (reg_t*) &(save_struc->last)); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_list_t(FILE *fh, list_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record list_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "first")) { + if (read_reg_t(fh, (reg_t*) &(save_struc->first), value, line, hiteof)) { + _cfsml_error("Token expected by read_reg_t() for first at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "last")) { + if (read_reg_t(fh, (reg_t*) &(save_struc->last), value, line, hiteof)) { + _cfsml_error("Token expected by read_reg_t() for last at line %d\n", *line); + return CFSML_FAILURE; + } + } else + { + _cfsml_error("list_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_sys_string_t(FILE *fh, sys_string_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "name = "); + _cfsml_write_string(fh, (char **) &(save_struc->name)); + fprintf(fh, "\n"); + fprintf(fh, "max_size = "); + _cfsml_write_int(fh, (int*) &(save_struc->max_size)); + fprintf(fh, "\n"); + fprintf(fh, "value = "); + _cfsml_write_string(fh, (char **) &(save_struc->value)); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_sys_string_t(FILE *fh, sys_string_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record sys_string_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "name")) { + if (_cfsml_read_string(fh, (char **) &(save_struc->name), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_string() for name at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "max_size")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->max_size), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for max_size at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "value")) { + if (_cfsml_read_string(fh, (char **) &(save_struc->value), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_string() for value at line %d\n", *line); + return CFSML_FAILURE; + } + } else + { + _cfsml_error("sys_string_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_script_t(FILE *fh, script_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "nr = "); + _cfsml_write_int(fh, (int*) &(save_struc->nr)); + fprintf(fh, "\n"); + fprintf(fh, "buf_size = "); + _cfsml_write_size_t(fh, (size_t*) &(save_struc->buf_size)); + fprintf(fh, "\n"); + fprintf(fh, "script_size = "); + _cfsml_write_size_t(fh, (size_t*) &(save_struc->script_size)); + fprintf(fh, "\n"); + fprintf(fh, "heap_size = "); + _cfsml_write_size_t(fh, (size_t*) &(save_struc->heap_size)); + fprintf(fh, "\n"); + fprintf(fh, "obj_indices = "); + write_int_hash_map_tp(fh, (int_hash_map_t **) &(save_struc->obj_indices)); + fprintf(fh, "\n"); + fprintf(fh, "exports_nr = "); + _cfsml_write_int(fh, (int*) &(save_struc->exports_nr)); + fprintf(fh, "\n"); + fprintf(fh, "synonyms_nr = "); + _cfsml_write_int(fh, (int*) &(save_struc->synonyms_nr)); + fprintf(fh, "\n"); + fprintf(fh, "lockers = "); + _cfsml_write_int(fh, (int*) &(save_struc->lockers)); + fprintf(fh, "\n"); + fprintf(fh, "objects_allocated = "); + _cfsml_write_int(fh, (int*) &(save_struc->objects_allocated)); + fprintf(fh, "\n"); + fprintf(fh, "objects_nr = "); + _cfsml_write_int(fh, (int*) &(save_struc->objects_nr)); + fprintf(fh, "\n"); + fprintf(fh, "objects = "); + min = max = save_struc->objects_allocated; + if (!save_struc->objects) + min = max = 0; /* Don't write if it points to NULL */ + fprintf(fh, "[%d][\n", max); + for (i = 0; i < min; i++) { + _cfsml_write_object_t(fh, &(save_struc->objects[i])); + fprintf(fh, "\n"); + } + fprintf(fh, "]"); + fprintf(fh, "\n"); + fprintf(fh, "locals_offset = "); + _cfsml_write_int(fh, (int*) &(save_struc->locals_offset)); + fprintf(fh, "\n"); + fprintf(fh, "locals_segment = "); + _cfsml_write_int(fh, (int*) &(save_struc->locals_segment)); + fprintf(fh, "\n"); + fprintf(fh, "marked_as_deleted = "); + _cfsml_write_int(fh, (int*) &(save_struc->marked_as_deleted)); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_script_t(FILE *fh, script_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record script_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "nr")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->nr), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for nr at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "buf_size")) { + if (_cfsml_read_size_t(fh, (size_t*) &(save_struc->buf_size), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_size_t() for buf_size at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "script_size")) { + if (_cfsml_read_size_t(fh, (size_t*) &(save_struc->script_size), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_size_t() for script_size at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "heap_size")) { + if (_cfsml_read_size_t(fh, (size_t*) &(save_struc->heap_size), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_size_t() for heap_size at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "obj_indices")) { + if (read_int_hash_map_tp(fh, (int_hash_map_t **) &(save_struc->obj_indices), value, line, hiteof)) { + _cfsml_error("Token expected by read_int_hash_map_tp() for obj_indices at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "exports_nr")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->exports_nr), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for exports_nr at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "synonyms_nr")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->synonyms_nr), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for synonyms_nr at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "lockers")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->lockers), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for lockers at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "objects_allocated")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->objects_allocated), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for objects_allocated at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "objects_nr")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->objects_nr), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for objects_nr at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "objects")) { + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } + /* Prepare to restore dynamic array */ + max = strtol(value + 1, NULL, 0); + if (max < 0) { + _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line); + return CFSML_FAILURE; + } + + if (max) { + save_struc->objects = (object_t *) sci_malloc(max * sizeof(object_t)); +#ifdef SATISFY_PURIFY + memset(save_struc->objects, 0, max * sizeof(object_t)); +#endif + _cfsml_register_pointer(save_struc->objects); + } + else + save_struc->objects = NULL; + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { + _cfsml_error("Token expected at line %d\n", *line); + return 1; + } + if (strcmp(value, "]")) { + if (i == max) { + _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line); + return CFSML_FAILURE; + } + if (_cfsml_read_object_t(fh, &(save_struc->objects[i++]), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_object_t() for objects[i++] at line %d\n", *line); + return CFSML_FAILURE; + } + } else done = 1; + } while (!done); + save_struc->objects_allocated = max ; /* Set array size accordingly */ + } else + if (!strcmp(token, "locals_offset")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->locals_offset), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for locals_offset at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "locals_segment")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->locals_segment), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for locals_segment at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "marked_as_deleted")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->marked_as_deleted), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for marked_as_deleted at line %d\n", *line); + return CFSML_FAILURE; + } + } else + { + _cfsml_error("script_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + +static void +_cfsml_write_seg_manager_t(FILE *fh, seg_manager_t* save_struc) +{ + int min, max, i; + + fprintf(fh, "{\n"); + fprintf(fh, "id_seg_map = "); + write_int_hash_map_tp(fh, (int_hash_map_t **) &(save_struc->id_seg_map)); + fprintf(fh, "\n"); + fprintf(fh, "heap = "); + min = max = save_struc->heap_size; + if (!save_struc->heap) + min = max = 0; /* Don't write if it points to NULL */ + fprintf(fh, "[%d][\n", max); + for (i = 0; i < min; i++) { + write_mem_obj_tp(fh, &(save_struc->heap[i])); + fprintf(fh, "\n"); + } + fprintf(fh, "]"); + fprintf(fh, "\n"); + fprintf(fh, "heap_size = "); + _cfsml_write_int(fh, (int*) &(save_struc->heap_size)); + fprintf(fh, "\n"); + fprintf(fh, "reserved_id = "); + _cfsml_write_int(fh, (int*) &(save_struc->reserved_id)); + fprintf(fh, "\n"); + fprintf(fh, "exports_wide = "); + _cfsml_write_int(fh, (int*) &(save_struc->exports_wide)); + fprintf(fh, "\n"); + fprintf(fh, "sci1_1 = "); + _cfsml_write_int(fh, (int*) &(save_struc->sci1_1)); + fprintf(fh, "\n"); + fprintf(fh, "gc_mark_bits = "); + _cfsml_write_int(fh, (int*) &(save_struc->gc_mark_bits)); + fprintf(fh, "\n"); + fprintf(fh, "mem_allocated = "); + _cfsml_write_size_t(fh, (size_t*) &(save_struc->mem_allocated)); + fprintf(fh, "\n"); + fprintf(fh, "clones_seg_id = "); + _cfsml_write_seg_id_t(fh, (seg_id_t*) &(save_struc->clones_seg_id)); + fprintf(fh, "\n"); + fprintf(fh, "lists_seg_id = "); + _cfsml_write_seg_id_t(fh, (seg_id_t*) &(save_struc->lists_seg_id)); + fprintf(fh, "\n"); + fprintf(fh, "nodes_seg_id = "); + _cfsml_write_seg_id_t(fh, (seg_id_t*) &(save_struc->nodes_seg_id)); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +static int +_cfsml_read_seg_manager_t(FILE *fh, seg_manager_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; + int assignment, closed, done; + + if (strcmp(lastval, "{")) { + _cfsml_error("Reading record seg_manager_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval); + return CFSML_FAILURE; + }; + closed = 0; + do { + char *value; + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + + if (!token) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!assignment) { + if (!strcmp(token, "}")) + closed = 1; + else { + _cfsml_error("Expected assignment or closing braces in line %d\n", *line); + return CFSML_FAILURE; + } + } else { + value = ""; + while (!value || !strcmp(value, "")) + value = _cfsml_get_value(fh, line, hiteof); + if (!value) { + _cfsml_error("Expected token at line %d\n", *line); + return CFSML_FAILURE; + } + if (!strcmp(token, "id_seg_map")) { + if (read_int_hash_map_tp(fh, (int_hash_map_t **) &(save_struc->id_seg_map), value, line, hiteof)) { + _cfsml_error("Token expected by read_int_hash_map_tp() for id_seg_map at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "heap")) { + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } + /* Prepare to restore dynamic array */ + max = strtol(value + 1, NULL, 0); + if (max < 0) { + _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line); + return CFSML_FAILURE; + } + + if (max) { + save_struc->heap = (mem_obj_ptr *) sci_malloc(max * sizeof(mem_obj_ptr)); +#ifdef SATISFY_PURIFY + memset(save_struc->heap, 0, max * sizeof(mem_obj_ptr)); +#endif + _cfsml_register_pointer(save_struc->heap); + } + else + save_struc->heap = NULL; + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { + _cfsml_error("Token expected at line %d\n", *line); + return 1; + } + if (strcmp(value, "]")) { + if (i == max) { + _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line); + return CFSML_FAILURE; + } + if (read_mem_obj_tp(fh, &(save_struc->heap[i++]), value, line, hiteof)) { + _cfsml_error("Token expected by read_mem_obj_tp() for heap[i++] at line %d\n", *line); + return CFSML_FAILURE; + } + } else done = 1; + } while (!done); + save_struc->heap_size = max ; /* Set array size accordingly */ + } else + if (!strcmp(token, "heap_size")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->heap_size), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for heap_size at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "reserved_id")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->reserved_id), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for reserved_id at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "exports_wide")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->exports_wide), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for exports_wide at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "sci1_1")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->sci1_1), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for sci1_1 at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "gc_mark_bits")) { + if (_cfsml_read_int(fh, (int*) &(save_struc->gc_mark_bits), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_int() for gc_mark_bits at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "mem_allocated")) { + if (_cfsml_read_size_t(fh, (size_t*) &(save_struc->mem_allocated), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_size_t() for mem_allocated at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "clones_seg_id")) { + if (_cfsml_read_seg_id_t(fh, (seg_id_t*) &(save_struc->clones_seg_id), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_seg_id_t() for clones_seg_id at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "lists_seg_id")) { + if (_cfsml_read_seg_id_t(fh, (seg_id_t*) &(save_struc->lists_seg_id), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_seg_id_t() for lists_seg_id at line %d\n", *line); + return CFSML_FAILURE; + } + } else + if (!strcmp(token, "nodes_seg_id")) { + if (_cfsml_read_seg_id_t(fh, (seg_id_t*) &(save_struc->nodes_seg_id), value, line, hiteof)) { + _cfsml_error("Token expected by _cfsml_read_seg_id_t() for nodes_seg_id at line %d\n", *line); + return CFSML_FAILURE; + } + } else + { + _cfsml_error("seg_manager_t: Assignment to invalid identifier '%s' in line %d\n", token, *line); + return CFSML_FAILURE; + } + } + } while (!closed); /* Until closing braces are hit */ + return CFSML_SUCCESS; +} + + +/* Auto-generated CFSML declaration and function block ends here */ +/* Auto-generation performed by cfsml.pl 0.8.2 */ + +void +write_songlib_t(FILE *fh, songlib_t *songlib) +{ + song_t *seeker = *(songlib->lib); + int songcount = song_lib_count(*songlib); + + fprintf(fh, "{\n"); + fprintf(fh, "songcount = %d\n", songcount); + fprintf(fh, "list = \n"); + fprintf(fh, "[\n"); + while (seeker) + { + seeker->restore_time = seeker->it->get_timepos(seeker->it); +/* Auto-generated CFSML data writer code */ + _cfsml_write_song_t(fh, seeker); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ + seeker = seeker->next; + } + fprintf(fh, "]\n"); + fprintf(fh, "}\n"); +} + +int +read_songlib_t(FILE *fh, songlib_t *songlib, char *lastval, int *line, int *hiteof) +{ + int songcount; + int i; + song_t *newsong; + int oldstatus; + + fscanf(fh, "{\n"); + fscanf(fh, "songcount = %d\n", &songcount); + fscanf(fh, "list = \n"); + fscanf(fh, "[\n"); + *line += 4; + song_lib_init(songlib); + for (i = 0; i < songcount; i++) + { +/* Auto-generated CFSML data reader code */ + { + int _cfsml_eof = 0, _cfsml_error; + int dummy; + char *_cfsml_inp = lastval; + _cfsml_error = read_song_tp(fh, &newsong, _cfsml_inp, &(*line), &_cfsml_eof); + *hiteof = _cfsml_error; + if (_cfsml_last_value_retreived) { + free(_cfsml_last_value_retreived); + _cfsml_last_value_retreived = NULL; + } + if (_cfsml_last_identifier_retreived) { + free(_cfsml_last_identifier_retreived); + _cfsml_last_identifier_retreived = NULL; + } + } +/* End of auto-generated CFSML data reader code */ + song_lib_add(*songlib, newsong); + } + fscanf(fh, "]\n"); + fscanf(fh, "}\n");; + *line += 2; + return 0; +} + +struct { + int type; + char *name; +} mem_obj_string_names[] = { + {MEM_OBJ_INVALID, "INVALID"}, + {MEM_OBJ_SCRIPT, "SCRIPT"}, + {MEM_OBJ_CLONES, "CLONES"}, + {MEM_OBJ_LOCALS, "LOCALS"}, + {MEM_OBJ_STACK, "STACK"}, + {MEM_OBJ_SYS_STRINGS,"SYS_STRINGS"}, + {MEM_OBJ_LISTS,"LISTS"}, + {MEM_OBJ_NODES,"NODES"}, + {MEM_OBJ_HUNK,"HUNK"}, + {MEM_OBJ_DYNMEM,"DYNMEM"}}; + +int +mem_obj_string_to_enum(char *str) +{ + int i; + + for (i = 0; i <= MEM_OBJ_MAX; i++) + { if (!strcasecmp(mem_obj_string_names[i].name, str)) + return i; + } + + return -1; +} + +static int bucket_length; + +void +write_int_hash_map_tp(FILE *fh, int_hash_map_t **foo) +{ +/* Auto-generated CFSML data writer code */ + _cfsml_write_int_hash_map_t(fh, *foo); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ +} + +void +write_song_tp(FILE *fh, song_t **foo) +{ +/* Auto-generated CFSML data writer code */ + _cfsml_write_song_t(fh, *foo); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ +} + +song_iterator_t * +build_iterator(state_t *s, int song_nr, int type, songit_id_t id); + +int +read_song_tp(FILE *fh, song_t **foo, char *lastval, int *line, int *hiteof) +{ + char *token; + int assignment; + *foo = (song_t*) malloc(sizeof(song_t)); + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); +/* Auto-generated CFSML data reader code */ + { + int _cfsml_eof = 0, _cfsml_error; + int dummy; + char *_cfsml_inp = token; + _cfsml_error = _cfsml_read_song_t(fh, (*foo), _cfsml_inp, &(*line), &_cfsml_eof); + *hiteof = _cfsml_error; + if (_cfsml_last_value_retreived) { + free(_cfsml_last_value_retreived); + _cfsml_last_value_retreived = NULL; + } + if (_cfsml_last_identifier_retreived) { + free(_cfsml_last_identifier_retreived); + _cfsml_last_identifier_retreived = NULL; + } + } +/* End of auto-generated CFSML data reader code */ + (*foo)->delay = 0; + (*foo)->it = NULL; + (*foo)->next_playing = (*foo)->next_stopping = (*foo)->next = NULL; + return 0; +} +int +read_int_hash_map_tp(FILE *fh, int_hash_map_t **foo, char *lastval, int *line, int *hiteof) +{ + *foo = (int_hash_map_t*)malloc(sizeof(int_hash_map_t)); +/* Auto-generated CFSML data reader code */ + { + int _cfsml_eof = 0, _cfsml_error; + int dummy; + char *_cfsml_inp = lastval; + _cfsml_error = _cfsml_read_int_hash_map_t(fh, (*foo), _cfsml_inp, &(*line), &_cfsml_eof); + *hiteof = _cfsml_error; + if (_cfsml_last_value_retreived) { + free(_cfsml_last_value_retreived); + _cfsml_last_value_retreived = NULL; + } + if (_cfsml_last_identifier_retreived) { + free(_cfsml_last_identifier_retreived); + _cfsml_last_identifier_retreived = NULL; + } + } +/* End of auto-generated CFSML data reader code */ + (*foo)->holes = NULL; + return 0; +} + +void +write_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo) +{ + if (!(*foo)) + { + fputs("\\null", fh); + } else + { + fprintf(fh,"[\n%d=>%d\n", (*foo)->name, (*foo)->value); + if ((*foo)->next) + { +/* Auto-generated CFSML data writer code */ + write_int_hash_map_node_tp(fh, &((*foo)->next)); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ + } else fputc('L', fh); + fputs("]", fh); + } +} + +int +read_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo, char *lastval, int *line, int *hiteof) +{ + static char buffer[80]; + + if (lastval[0] == '\\') { + *foo = NULL; /* No hash map node */ + } else { + *foo = (int_hash_map_node_t*)malloc(sizeof(int_hash_map_node_t)); + if (lastval[0] != '[') + { + sciprintf("Expected opening bracket in hash_map_node_t on line %d\n", *line); + return 1; + } + + do { + (*line)++; + fgets(buffer, 80, fh); + if (buffer[0] == 'L') + { + (*foo)->next = NULL; + buffer[0] = buffer[1]; + } /* HACK: deliberately no else clause here */ + if (buffer[0] == ']') + { + break; + } + else if (buffer[0] == '[') + { + if (read_int_hash_map_node_tp(fh, &((*foo)->next), buffer, line, hiteof)) + return 1; + } + else if (sscanf(buffer, "%d=>%d", &((*foo)->name), &((*foo)->value))<2) + { + sciprintf("Error parsing hash_map_node_t on line %d\n", *line); + return 1; + } + } while (1); + } + + return 0; +} + +void +write_menubar_tp(FILE *fh, menubar_t **foo) +{ + if (*foo) { + +/* Auto-generated CFSML data writer code */ + _cfsml_write_menubar_t(fh, (*foo)); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ + + } else { /* Nothing to write */ + fputs("\\null\\", fh); + } +} + + +int +read_menubar_tp(FILE *fh, menubar_t **foo, char *lastval, int *line, int *hiteof) +{ + + if (lastval[0] == '\\') { + *foo = NULL; /* No menu bar */ + } else { + + *foo = (menubar_t *) sci_malloc(sizeof(menubar_t)); +/* Auto-generated CFSML data reader code */ + { + int _cfsml_eof = 0, _cfsml_error; + int dummy; + char *_cfsml_inp = lastval; + _cfsml_error = _cfsml_read_menubar_t(fh, (*foo), _cfsml_inp, &(*line), &_cfsml_eof); + *hiteof = _cfsml_error; + if (_cfsml_last_value_retreived) { + free(_cfsml_last_value_retreived); + _cfsml_last_value_retreived = NULL; + } + if (_cfsml_last_identifier_retreived) { + free(_cfsml_last_identifier_retreived); + _cfsml_last_identifier_retreived = NULL; + } + } +/* End of auto-generated CFSML data reader code */ + + } + return *hiteof; +} + +void +write_mem_obj_t(FILE *fh, mem_obj_t *foo) +{ + fprintf(fh, "%s\n", mem_obj_string_names[foo->type].name); +/* Auto-generated CFSML data writer code */ + _cfsml_write_int(fh, &foo->segmgr_id); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ + switch (foo->type) + { + case MEM_OBJ_SCRIPT: +/* Auto-generated CFSML data writer code */ + _cfsml_write_script_t(fh, &foo->data.script); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ + break; + case MEM_OBJ_CLONES: +/* Auto-generated CFSML data writer code */ + _cfsml_write_clone_table_t(fh, &foo->data.clones); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ + break; + case MEM_OBJ_LOCALS: +/* Auto-generated CFSML data writer code */ + _cfsml_write_local_variables_t(fh, &foo->data.locals); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ + break; + case MEM_OBJ_SYS_STRINGS: +/* Auto-generated CFSML data writer code */ + _cfsml_write_sys_strings_t(fh, &foo->data.sys_strings); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ + break; + case MEM_OBJ_STACK: +/* Auto-generated CFSML data writer code */ + _cfsml_write_int(fh, &foo->data.stack.nr); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ + break; + case MEM_OBJ_HUNK: + break; + case MEM_OBJ_LISTS: +/* Auto-generated CFSML data writer code */ + _cfsml_write_list_table_t(fh, &foo->data.lists); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ + break; + case MEM_OBJ_NODES: +/* Auto-generated CFSML data writer code */ + _cfsml_write_node_table_t(fh, &foo->data.nodes); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ + break; + case MEM_OBJ_DYNMEM: +/* Auto-generated CFSML data writer code */ + _cfsml_write_dynmem_t(fh, &foo->data.dynmem); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ + break; + } +} + +int +read_mem_obj_t(FILE *fh, mem_obj_t *foo, char *lastval, int *line, int *hiteof) +{ + char buffer[80]; + foo->type = mem_obj_string_to_enum(lastval); + if (foo->type < 0) + { + sciprintf("Unknown mem_obj_t type %s on line %d\n", lastval, *line); + return 1; + } + +/* Auto-generated CFSML data reader code */ + { + int _cfsml_eof = 0, _cfsml_error; + int dummy; + char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); + + _cfsml_error = _cfsml_read_int(fh, &foo->segmgr_id, _cfsml_inp, &(*line), &_cfsml_eof); + *hiteof = _cfsml_error; + if (_cfsml_last_value_retreived) { + free(_cfsml_last_value_retreived); + _cfsml_last_value_retreived = NULL; + } + if (_cfsml_last_identifier_retreived) { + free(_cfsml_last_identifier_retreived); + _cfsml_last_identifier_retreived = NULL; + } + } +/* End of auto-generated CFSML data reader code */ + switch (foo->type) + { + case MEM_OBJ_SCRIPT: +/* Auto-generated CFSML data reader code */ + { + int _cfsml_eof = 0, _cfsml_error; + int dummy; + char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); + + _cfsml_error = _cfsml_read_script_t(fh, &foo->data.script, _cfsml_inp, &(*line), &_cfsml_eof); + *hiteof = _cfsml_error; + if (_cfsml_last_value_retreived) { + free(_cfsml_last_value_retreived); + _cfsml_last_value_retreived = NULL; + } + if (_cfsml_last_identifier_retreived) { + free(_cfsml_last_identifier_retreived); + _cfsml_last_identifier_retreived = NULL; + } + } +/* End of auto-generated CFSML data reader code */ + break; + case MEM_OBJ_CLONES: +/* Auto-generated CFSML data reader code */ + { + int _cfsml_eof = 0, _cfsml_error; + int dummy; + char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); + + _cfsml_error = _cfsml_read_clone_table_t(fh, &foo->data.clones, _cfsml_inp, &(*line), &_cfsml_eof); + *hiteof = _cfsml_error; + if (_cfsml_last_value_retreived) { + free(_cfsml_last_value_retreived); + _cfsml_last_value_retreived = NULL; + } + if (_cfsml_last_identifier_retreived) { + free(_cfsml_last_identifier_retreived); + _cfsml_last_identifier_retreived = NULL; + } + } +/* End of auto-generated CFSML data reader code */ + break; + case MEM_OBJ_LOCALS: +/* Auto-generated CFSML data reader code */ + { + int _cfsml_eof = 0, _cfsml_error; + int dummy; + char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); + + _cfsml_error = _cfsml_read_local_variables_t(fh, &foo->data.locals, _cfsml_inp, &(*line), &_cfsml_eof); + *hiteof = _cfsml_error; + if (_cfsml_last_value_retreived) { + free(_cfsml_last_value_retreived); + _cfsml_last_value_retreived = NULL; + } + if (_cfsml_last_identifier_retreived) { + free(_cfsml_last_identifier_retreived); + _cfsml_last_identifier_retreived = NULL; + } + } +/* End of auto-generated CFSML data reader code */ + break; + case MEM_OBJ_SYS_STRINGS: +/* Auto-generated CFSML data reader code */ + { + int _cfsml_eof = 0, _cfsml_error; + int dummy; + char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); + + _cfsml_error = _cfsml_read_sys_strings_t(fh, &foo->data.sys_strings, _cfsml_inp, &(*line), &_cfsml_eof); + *hiteof = _cfsml_error; + if (_cfsml_last_value_retreived) { + free(_cfsml_last_value_retreived); + _cfsml_last_value_retreived = NULL; + } + if (_cfsml_last_identifier_retreived) { + free(_cfsml_last_identifier_retreived); + _cfsml_last_identifier_retreived = NULL; + } + } +/* End of auto-generated CFSML data reader code */ + break; + case MEM_OBJ_LISTS: +/* Auto-generated CFSML data reader code */ + { + int _cfsml_eof = 0, _cfsml_error; + int dummy; + char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); + + _cfsml_error = _cfsml_read_list_table_t(fh, &foo->data.lists, _cfsml_inp, &(*line), &_cfsml_eof); + *hiteof = _cfsml_error; + if (_cfsml_last_value_retreived) { + free(_cfsml_last_value_retreived); + _cfsml_last_value_retreived = NULL; + } + if (_cfsml_last_identifier_retreived) { + free(_cfsml_last_identifier_retreived); + _cfsml_last_identifier_retreived = NULL; + } + } +/* End of auto-generated CFSML data reader code */ + break; + case MEM_OBJ_NODES: +/* Auto-generated CFSML data reader code */ + { + int _cfsml_eof = 0, _cfsml_error; + int dummy; + char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); + + _cfsml_error = _cfsml_read_node_table_t(fh, &foo->data.nodes, _cfsml_inp, &(*line), &_cfsml_eof); + *hiteof = _cfsml_error; + if (_cfsml_last_value_retreived) { + free(_cfsml_last_value_retreived); + _cfsml_last_value_retreived = NULL; + } + if (_cfsml_last_identifier_retreived) { + free(_cfsml_last_identifier_retreived); + _cfsml_last_identifier_retreived = NULL; + } + } +/* End of auto-generated CFSML data reader code */ + break; + case MEM_OBJ_STACK: +/* Auto-generated CFSML data reader code */ + { + int _cfsml_eof = 0, _cfsml_error; + int dummy; + char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); + + _cfsml_error = _cfsml_read_int(fh, &foo->data.stack.nr, _cfsml_inp, &(*line), &_cfsml_eof); + *hiteof = _cfsml_error; + if (_cfsml_last_value_retreived) { + free(_cfsml_last_value_retreived); + _cfsml_last_value_retreived = NULL; + } + if (_cfsml_last_identifier_retreived) { + free(_cfsml_last_identifier_retreived); + _cfsml_last_identifier_retreived = NULL; + } + } +/* End of auto-generated CFSML data reader code */ + foo->data.stack.entries = (reg_t *)sci_calloc(foo->data.stack.nr, sizeof(reg_t)); + break; + case MEM_OBJ_HUNK: + init_hunk_table(&foo->data.hunks); + break; + case MEM_OBJ_DYNMEM: +/* Auto-generated CFSML data reader code */ + { + int _cfsml_eof = 0, _cfsml_error; + int dummy; + char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); + + _cfsml_error = _cfsml_read_dynmem_t(fh, &foo->data.dynmem, _cfsml_inp, &(*line), &_cfsml_eof); + *hiteof = _cfsml_error; + if (_cfsml_last_value_retreived) { + free(_cfsml_last_value_retreived); + _cfsml_last_value_retreived = NULL; + } + if (_cfsml_last_identifier_retreived) { + free(_cfsml_last_identifier_retreived); + _cfsml_last_identifier_retreived = NULL; + } + } +/* End of auto-generated CFSML data reader code */ + break; + } + + return *hiteof; +} + +void +write_mem_obj_tp(FILE *fh, mem_obj_t **foo) +{ + if (*foo) { + +/* Auto-generated CFSML data writer code */ + write_mem_obj_t(fh, (*foo)); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ + + } else { /* Nothing to write */ + fputs("\\null\\", fh); + } +} + +int +read_mem_obj_tp(FILE *fh, mem_obj_t **foo, char *lastval, int *line, int *hiteof) +{ + + if (lastval[0] == '\\') { + *foo = NULL; /* No menu bar */ + } else { + *foo = (mem_obj_t *) sci_malloc(sizeof(mem_obj_t)); +/* Auto-generated CFSML data reader code */ + { + int _cfsml_eof = 0, _cfsml_error; + int dummy; + char *_cfsml_inp = lastval; + _cfsml_error = read_mem_obj_t(fh, (*foo), _cfsml_inp, &(*line), &_cfsml_eof); + *hiteof = _cfsml_error; + if (_cfsml_last_value_retreived) { + free(_cfsml_last_value_retreived); + _cfsml_last_value_retreived = NULL; + } + if (_cfsml_last_identifier_retreived) { + free(_cfsml_last_identifier_retreived); + _cfsml_last_identifier_retreived = NULL; + } + } +/* End of auto-generated CFSML data reader code */ + return *hiteof; + } + return 0; +} + + + +/* This function is called to undo some strange stuff done in preparation +** to writing a gamestate to disk +*/ +void +_gamestate_unfrob(state_t *s) +{ +} + + +int +gamestate_save(state_t *s, char *dirname) +{ + FILE *fh; + sci_dir_t dir; + char *filename; + int fd; + + _global_save_state = s; + s->savegame_version = FREESCI_CURRENT_SAVEGAME_VERSION; + s->dyn_views_list_serial = (s->dyn_views)? s->dyn_views->serial : -2; + s->drop_views_list_serial = (s->drop_views)? s->drop_views->serial : -2; + s->port_serial = (s->port)? s->port->serial : -2; + + if (s->execution_stack_base) { + sciprintf("Cannot save from below kernel function\n"); + return 1; + } + + scimkdir (dirname, 0700); + + if (chdir (dirname)) { + sciprintf("Could not enter directory '%s'\n", dirname); + return 1; + } + + sci_init_dir(&dir); + filename = sci_find_first(&dir, "*"); + while (filename) { + if (strcmp(filename, "..") && strcmp(filename, ".")) + unlink(filename); /* Delete all files in directory */ + filename = sci_find_next(&dir); + } + sci_finish_find(&dir); + +/* + if (s->sound_server) { + if ((s->sound_server->save)(s, dirname)) { + sciprintf("Saving failed for the sound subsystem\n"); + chdir (".."); + return 1; + } + } +*/ + fh = fopen("state", "w" FO_TEXT); + + /* Calculate the time spent with this game */ + s->game_time = time(NULL) - s->game_start_time.tv_sec; + +SCI_MEMTEST; +/* Auto-generated CFSML data writer code */ + _cfsml_write_state_t(fh, s); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ +SCI_MEMTEST; + + fclose(fh); + + _gamestate_unfrob(s); + + + chdir (".."); + return 0; +} + +static seg_id_t +find_unique_seg_by_type(seg_manager_t *self, int type) +{ + int i; + + for (i = 0; i < self->heap_size; i++) + if (self->heap[i] && + self->heap[i]->type == type) + return i; + return -1; +} + +static byte * +find_unique_script_block(state_t *s, byte *buf, int type) +{ + int magic_pos_adder = s->version >= SCI_VERSION_FTU_NEW_SCRIPT_HEADER ? 0 : 2; + + buf += magic_pos_adder; + do { + int seeker_type = getUInt16(buf); + int seeker_size; + + if (seeker_type == 0) break; + if (seeker_type == type) return buf; + + seeker_size = getUInt16(buf + 2); + buf += seeker_size; + } while(1); + + return NULL; +} + +static +void reconstruct_stack(state_t *retval) +{ + seg_id_t stack_seg = find_unique_seg_by_type(&retval->seg_manager, MEM_OBJ_STACK); + dstack_t *stack = &(retval->seg_manager.heap[stack_seg]->data.stack); + + retval->stack_segment = stack_seg; + retval->stack_base = stack->entries; + retval->stack_top = retval->stack_base + VM_STACK_SIZE; +} + +static +int clone_entry_used(clone_table_t *table, int n) +{ + int backup; + int seeker = table->first_free; + clone_entry_t *entries = table->table; + + if (seeker == HEAPENTRY_INVALID) return 1; + + do { + if (seeker == n) return 0; + backup = seeker; + seeker = entries[seeker].next_free; + } while (entries[backup].next_free != HEAPENTRY_INVALID); + + return 1; +} + +static +void load_script(state_t *s, seg_id_t seg) +{ + resource_t *script, *heap; + script_t *scr = &(s->seg_manager.heap[seg]->data.script); + + scr->buf = (byte *) malloc(scr->buf_size); + + script = scir_find_resource(s->resmgr, sci_script, scr->nr, 0); + if (s->version >= SCI_VERSION(1,001,000)) + heap = scir_find_resource(s->resmgr, sci_heap, scr->nr, 0); + + switch (s->seg_manager.sci1_1) + { + case 0 : + sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, seg, SEG_ID); + break; + case 1 : + sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, seg, SEG_ID); + sm_mcpy_in_out( &s->seg_manager, scr->script_size, heap->data, heap->size, seg, SEG_ID); + break; + } +} + +static +void reconstruct_scripts(state_t *s, seg_manager_t *self) +{ + int i; + mem_obj_t *mobj; + object_t **objects; + int *objects_nr; + for (i = 0; i < self->heap_size; i++) + if (self->heap[i]) + { + mobj = self->heap[i]; + switch (mobj->type) + { + case MEM_OBJ_SCRIPT: + { + int j; + script_t *scr = &mobj->data.script; + + load_script(s, i); + scr->locals_block = scr->locals_segment == 0 ? NULL : + &s->seg_manager.heap[scr->locals_segment]->data.locals; + scr->export_table = (guint16 *) find_unique_script_block(s, scr->buf, sci_obj_exports); + scr->synonyms = find_unique_script_block(s, scr->buf, sci_obj_synonyms); + scr->code = NULL; + scr->code_blocks_nr = 0; + scr->code_blocks_allocated = 0; + + if (!self->sci1_1) + scr->export_table += 3; + + for (j = 0; j < scr->objects_nr; j++) + { + byte *data = scr->buf + scr->objects[j].pos.offset; + scr->objects[j].base = scr->buf; + scr->objects[j].base_obj = data; + } + + } + } + } + + for (i = 0; i < self->heap_size; i++) + if (self->heap[i]) + { + mobj = self->heap[i]; + switch (mobj->type) + { + case MEM_OBJ_SCRIPT: + { + int j; + script_t *scr = &mobj->data.script; + + for (j = 0; j < scr->objects_nr; j++) + { + byte *data = scr->buf + scr->objects[j].pos.offset; + + if (self->sci1_1) + { + guint16 *funct_area = (guint16 *) (scr->buf + getUInt16( data + 6 )); + guint16 *prop_area = (guint16 *) (scr->buf + getUInt16( data + 4 )); + + scr->objects[j].base_method = funct_area; + scr->objects[j].base_vars = prop_area; + } else + { + int funct_area = getUInt16( data + SCRIPT_FUNCTAREAPTR_OFFSET ); + object_t *base_obj; + + base_obj = obj_get(s, scr->objects[j].variables[SCRIPT_SPECIES_SELECTOR]); + + if (!base_obj) + { + sciprintf("Object without a base class: Script %d, index %d (reg address "PREG"\n", + scr->nr, j, PRINT_REG(scr->objects[j].variables[SCRIPT_SPECIES_SELECTOR])); + continue; + } + scr->objects[j].variable_names_nr = base_obj->variables_nr; + scr->objects[j].base_obj = base_obj->base_obj; + + scr->objects[j].base_method = (guint16 *) (data + funct_area); + scr->objects[j].base_vars = (guint16 *) (data + scr->objects[j].variable_names_nr * 2 + SCRIPT_SELECTOR_OFFSET); + } + } + } + } + } +} + +void +reconstruct_clones(state_t *s, seg_manager_t *self) +{ + int i; + mem_obj_t *mobj; + + for (i = 0; i < self->heap_size; i++) + if (self->heap[i]) + { + mobj = self->heap[i]; + switch (mobj->type) + { + case MEM_OBJ_CLONES: + { + int j; + clone_entry_t *seeker = mobj->data.clones.table; + + sciprintf("Free list: "); + for (j = mobj->data.clones.first_free; + j != HEAPENTRY_INVALID; + j = mobj->data.clones.table[j].next_free) + { + sciprintf("%d ", j); + } + sciprintf("\n"); + + sciprintf("Entries w/zero vars: "); + for (j = 0; j < mobj->data.clones.max_entry; j++) + { + if (mobj->data.clones.table[j].entry.variables == NULL) + sciprintf("%d ", j); + } + sciprintf("\n"); + + for (j = 0; j < mobj->data.clones.max_entry; j++) + { + object_t *base_obj; + + if (!clone_entry_used(&mobj->data.clones, j)) { + seeker++; + continue; + } + base_obj = obj_get(s, seeker->entry.variables[SCRIPT_SPECIES_SELECTOR]); + if (!base_obj) + { + sciprintf("Clone entry without a base class: %d\n", j); + seeker->entry.base = seeker->entry.base_obj = NULL; + seeker->entry.base_vars = seeker->entry.base_method = NULL; + continue; + } + seeker->entry.base = base_obj->base; + seeker->entry.base_obj = base_obj->base_obj; + seeker->entry.base_vars = base_obj->base_vars; + seeker->entry.base_method = base_obj->base_method; + + seeker++; + } + + break; + } + } + } +} + +int +_reset_graphics_input(state_t *s); + +song_iterator_t * +new_fast_forward_iterator(song_iterator_t *it, int delta); + +static +void reconstruct_sounds(state_t *s) +{ + song_t *seeker; + int it_type = s->resmgr->sci_version >= SCI_VERSION_01 ? + SCI_SONG_ITERATOR_TYPE_SCI1 + : SCI_SONG_ITERATOR_TYPE_SCI0; + + if (s->sound.songlib.lib) + seeker = *(s->sound.songlib.lib); + else + { + song_lib_init(&s->sound.songlib); + seeker = NULL; + } + while (seeker) + { + song_iterator_t *base, *ff; + int oldstatus; + song_iterator_message_t msg; + + base = ff = build_iterator(s, seeker->resource_num, it_type, seeker->handle); + if (seeker->restore_behavior == RESTORE_BEHAVIOR_CONTINUE) + ff = (song_iterator_t *) new_fast_forward_iterator(base, seeker->restore_time); + ff->init(ff); + + msg = songit_make_message(seeker->handle, SIMSG_SET_LOOPS(seeker->loops)); + songit_handle_message(&ff, msg); + msg = songit_make_message(seeker->handle, SIMSG_SET_HOLD(seeker->hold)); + songit_handle_message(&ff, msg); + + + oldstatus = seeker->status; + seeker->status = SOUND_STATUS_STOPPED; + seeker->it = ff; + sfx_song_set_status(&s->sound, seeker->handle, oldstatus); + seeker = seeker->next; + } + +} + +state_t * +gamestate_restore(state_t *s, char *dirname) +{ + FILE *fh; + int fd; + int i; + int read_eof = 0; + state_t *retval; + songlib_t temp; + + if (chdir (dirname)) { + sciprintf("Game state '%s' does not exist\n", dirname); + return NULL; + } + +/* + if (s->sound_server) { + if ((s->sound_server->restore)(s, dirname)) { + sciprintf("Restoring failed for the sound subsystem\n"); + return NULL; + } + } +*/ + + retval = (state_t *) sci_malloc(sizeof(state_t)); + + memset(retval, 0, sizeof(state_t)); + + retval->savegame_version = -1; + _global_save_state = retval; + retval->gfx_state = s->gfx_state; + + fh = fopen("state", "r" FO_TEXT); + if (!fh) { + free(retval); + return NULL; + } + + /* Backwards compatibility settings */ + retval->dyn_views = NULL; + retval->drop_views = NULL; + retval->port = NULL; + retval->save_dir_copy_buf = NULL; + + retval->sound_mute = s->sound_mute; + retval->sound_volume = s->sound_volume; + +/* Auto-generated CFSML data reader code */ + { + int _cfsml_line_ctr = 0; + struct _cfsml_pointer_refstruct **_cfsml_myptrrefptr = _cfsml_get_current_refpointer(); + int _cfsml_eof = 0, _cfsml_error; + int dummy; + char *_cfsml_inp = _cfsml_get_identifier(fh, &(_cfsml_line_ctr), &_cfsml_eof, &dummy); + + _cfsml_error = _cfsml_read_state_t(fh, retval, _cfsml_inp, &(_cfsml_line_ctr), &_cfsml_eof); + read_eof = _cfsml_error; + _cfsml_free_pointer_references(_cfsml_myptrrefptr, _cfsml_error); + if (_cfsml_last_value_retreived) { + free(_cfsml_last_value_retreived); + _cfsml_last_value_retreived = NULL; + } + if (_cfsml_last_identifier_retreived) { + free(_cfsml_last_identifier_retreived); + _cfsml_last_identifier_retreived = NULL; + } + } +/* End of auto-generated CFSML data reader code */ + + fclose(fh); + + if ((retval->savegame_version < FREESCI_MINIMUM_SAVEGAME_VERSION) || + (retval->savegame_version > FREESCI_CURRENT_SAVEGAME_VERSION)) { + + if (retval->savegame_version < FREESCI_MINIMUM_SAVEGAME_VERSION) + sciprintf("Old savegame version detected- can't load\n"); + else + sciprintf("Savegame version is %d- maximum supported is %0d\n", retval->savegame_version, FREESCI_CURRENT_SAVEGAME_VERSION); + + chdir(".."); + free(retval); + return NULL; + } + + sfx_exit(&s->sound); + _gamestate_unfrob(retval); + + /* Set exec stack base to zero */ + retval->execution_stack_base = 0; + retval->execution_stack_pos = 0; + + /* Now copy all current state information */ + /* Graphics and input state: */ + retval->animation_delay = s->animation_delay; + retval->animation_granularity = s->animation_granularity; + retval->gfx_state = s->gfx_state; + + retval->resmgr = s->resmgr; + + temp = retval->sound.songlib; + sfx_init(&retval->sound, retval->resmgr, s->sfx_init_flags); + retval->sfx_init_flags = s->sfx_init_flags; + song_lib_free(retval->sound.songlib); + retval->sound.songlib = temp; + + _reset_graphics_input(retval); + reconstruct_stack(retval); + reconstruct_scripts(retval, &retval->seg_manager); + reconstruct_clones(retval, &retval->seg_manager); + retval->game_obj = s->game_obj; + retval->script_000 = &retval->seg_manager.heap[script_get_segment(s, 0, SCRIPT_GET_DONT_LOAD)]->data.script; + retval->gc_countdown = GC_INTERVAL - 1; + retval->save_dir_copy = make_reg(s->sys_strings_segment, SYS_STRING_SAVEDIR); + retval->save_dir_edit_offset = 0; + retval->sys_strings_segment = find_unique_seg_by_type(&retval->seg_manager, MEM_OBJ_SYS_STRINGS); + retval->sys_strings = &(((mem_obj_t *)(GET_SEGMENT(retval->seg_manager, retval->sys_strings_segment, MEM_OBJ_SYS_STRINGS)))->data.sys_strings); + sys_strings_restore(retval->sys_strings, + s->sys_strings); + + /* Time state: */ + sci_get_current_time(&(retval->last_wait_time)); + retval->game_start_time.tv_sec = time(NULL) - retval->game_time; + retval->game_start_time.tv_usec = 0; + + /* File IO state: */ + retval->file_handles_nr = 2; + retval->file_handles = (FILE **)sci_calloc(2, sizeof(FILE *)); + + /* static parser information: */ + retval->parser_rules = s->parser_rules; + retval->parser_words_nr = s->parser_words_nr; + retval->parser_words = s->parser_words; + retval->parser_suffices_nr = s->parser_suffices_nr; + retval->parser_suffices = s->parser_suffices; + retval->parser_branches_nr = s->parser_branches_nr; + retval->parser_branches = s->parser_branches; + + /* static VM/Kernel information: */ + retval->selector_names_nr = s->selector_names_nr; + retval->selector_names = s->selector_names; + retval->kernel_names_nr = s->kernel_names_nr; + retval->kernel_names = s->kernel_names; + retval->kfunct_table = s->kfunct_table; + retval->kfunct_nr = s->kfunct_nr; + retval->opcodes = s->opcodes; + + memcpy(&(retval->selector_map), &(s->selector_map), sizeof(selector_map_t)); + + retval->max_version = retval->version; + retval->min_version = retval->version; + retval->parser_base = make_reg(s->sys_strings_segment, SYS_STRING_PARSER_BASE); + + /* Copy breakpoint information from current game instance */ + retval->have_bp = s->have_bp; + retval->bp_list = s->bp_list; + + retval->debug_mode = s->debug_mode; + + retval->resource_dir = s->resource_dir; + retval->work_dir = s->work_dir; + retval->kernel_opt_flags = 0; + retval->have_mouse_flag = s->have_mouse_flag; + + retval->successor = NULL; + retval->pic_priority_table = (int*)gfxop_get_pic_metainfo(retval->gfx_state); + retval->game_name = sci_strdup(obj_get_name(retval, retval->game_obj)); + + retval->sound.it = NULL; + retval->sound.flags = s->sound.flags; + retval->sound.song = NULL; + retval->sound.suspended = s->sound.suspended; + retval->sound.debug = s->sound.debug; + reconstruct_sounds(retval); + + chdir (".."); + + return retval; +} diff --git a/engines/sci/engine/savegame.cfsml b/engines/sci/engine/savegame.cfsml new file mode 100644 index 0000000000..57abbfb862 --- /dev/null +++ b/engines/sci/engine/savegame.cfsml @@ -0,0 +1,1226 @@ +/*************************************************************************** + savegame.cfsml Copyright (C) 1999 Christoph Reichenbach, TU Darmstadt + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ +/* Savegame handling for state_t structs. Makes heavy use of cfsml magic. */ +/* DON'T EDIT savegame.c ! Only modify savegame.cfsml, if something needs +** to be changed. Refer to freesci/docs/misc/cfsml.spec if you don't understand +** savegame.cfsml. If this doesn't solve your problem, contact the maintainer. +*/ + +#include +#include +#include +#include +#include +#include "heap.h" + +#ifdef _MSC_VER +#include +#endif + +#ifdef _WIN32 +#pragma warning( disable : 4101 ) +#endif + +#define HUNK_TYPE_GFX_SNAPSHOT_STRING "g\n" + +/* Missing: +** - SFXdriver +** - File input/output state (this is likely not to happen) +*/ + +static state_t *_global_save_state; +/* Needed for some graphical stuff. */ +#define FILE_VERSION _global_save_state->savegame_version + + +void +write_reg_t(FILE *fh, reg_t *foo) +{ + fprintf(fh, PREG, PRINT_REG(*foo)); +} + +int +read_reg_t(FILE *fh, reg_t *foo, char *lastval, int *line, int *hiteof) +{ + int segment, offset; + + if (sscanf(lastval, PREG, &segment, &offset)<2) + { + sciprintf("Error parsing reg_t on line %d\n", *line); + return 1; + } + + *foo = make_reg(segment, offset); + return 0; +} + +void +write_sci_version(FILE *fh, sci_version_t *foo) +{ + fprintf(fh, "%d.%03d.%03d", SCI_VERSION_MAJOR(*foo), SCI_VERSION_MINOR(*foo), + SCI_VERSION_PATCHLEVEL(*foo)); +} + +int +read_sci_version(FILE *fh, sci_version_t *foo, char *lastval, int *line, int *hiteof) +{ + return version_parse(lastval, foo); +} + +void +write_PTN(FILE *fh, parse_tree_node_t *foo) +{ + if (foo->type == PARSE_TREE_NODE_LEAF) + fprintf(fh, "L%d", foo->content.value); + else + fprintf(fh, "B(%d,%d)", foo->content.branches[0], foo->content.branches[1]); +} + +int +read_PTN(FILE *fh, parse_tree_node_t *foo, char *lastval, int *line, int *hiteof) +{ + if (lastval[0] == 'L') { + char *c = lastval + 1; + char *strend; + + while (*c && isspace(*c)) + ++c; + + if (!*c) + return 1; + + foo->content.value = strtol(c, &strend, 0); + + return (strend == c); /* Error if nothing could be read */ + + return 0; + } else if (lastval[0] == 'B') { + char *c = lastval + 1; + char *strend; + + while (*c && isspace(*c)) ++c; + if (*c++ != '(') return 1; + while (*c && isspace(*c)) ++c; + + foo->content.branches[0] = strtol(c, &strend, 0); + if (strend == c) + return 1; + c = strend; + + while (*c && isspace(*c)) ++c; + if (*c++ != ',') + return 1; + + while (*c && isspace(*c)) ++c; + + foo->content.branches[1] = strtol(c, &strend, 0); + if (strend == c) + return 1; + c = strend; + + while (*c && isspace(*c)) ++c; + if (*c++ != ')') return 1; + + return 0; + } else return 1; /* failure to parse anything */ +} + + +void +write_menubar_tp(FILE *fh, menubar_t **foo); +int +read_menubar_tp(FILE *fh, menubar_t **foo, char *lastval, int *line, int *hiteof); + +void +write_mem_obj_tp(FILE *fh, mem_obj_t **foo); +int +read_mem_obj_tp(FILE *fh, mem_obj_t **foo, char *lastval, int *line, int *hiteof); + +void +write_int_hash_map_tp(FILE *fh, int_hash_map_t **foo); +int +read_int_hash_map_tp(FILE *fh, int_hash_map_t **foo, char *lastval, int *line, int *hiteof); + +void +write_songlib_t(FILE *fh, songlib_t *foo); +int +read_songlib_t(FILE *fh, songlib_t *foo, char *lastval, int *line, int *hiteof); + +void +write_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo); +int +read_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo, char *lastval, int *line, int *hiteof); + +int +read_song_tp(FILE *fh, song_t **foo, char *lastval, int *line, int *hiteof); + +typedef mem_obj_t *mem_obj_ptr; + +%CFSML + +TYPE byte "byte" LIKE int; +TYPE long "long" LIKE int; +TYPE gint16 "gint16" LIKE int; +TYPE seg_id_t "seg_id_t" LIKE int; +TYPE sci_version_t "sci_version_t" USING write_sci_version read_sci_version; +TYPE menubar_tp "menubar_t *" USING write_menubar_tp read_menubar_tp; +TYPE mem_obj_t "mem_obj_t" USING write_mem_obj_t read_mem_obj_t; +TYPE mem_obj_ptr "mem_obj_t *" USING write_mem_obj_tp read_mem_obj_tp; +TYPE reg_t "reg_t" USING write_reg_t read_reg_t; +TYPE size_t "size_t" LIKE int; +TYPE int_hash_map_tp "int_hash_map_t *" USING write_int_hash_map_tp read_int_hash_map_tp; +TYPE int_hash_map_node_tp "int_hash_map_node_t *" USING write_int_hash_map_node_tp read_int_hash_map_node_tp; +TYPE songlib_t "songlib_t" USING write_songlib_t read_songlib_t; +TYPE song_tp "song_t *" USING write_song_tp read_song_tp; +TYPE song_iterator_t "song_iterator_t" USING write_song_iterator_t read_song_iterator_t; +TYPE song_handle_t "song_handle_t" LIKE int; + +RECORD song_t "song_t" { + song_handle_t handle; + int resource_num; + int priority; + int status; + int restore_behavior; + int restore_time; + int loops; + int hold; +} + +RECORD int_hash_map_t "int_hash_map_t" { + int base_value; + int_hash_map_node_tp nodes[STATIC DCS_INT_HASH_MAX+1]; +} + +RECORD menu_item_t "menu_item_t" { + int type; + string keytext; + int keytext_size; + + int flags; + byte said[STATIC MENU_SAID_SPEC_SIZE]; + reg_t said_pos; + string text; + reg_t text_pos; + int modifiers; + int key; + int enabled; + int tag; +} + +RECORD menu_t "menu_t" { + string title; + int title_width; + int width; + + menu_item_t items[DYNAMIC items_nr]; +} + +RECORD menubar_t "menubar_t" { + menu_t menus[DYNAMIC menus_nr]; +} + +RECORD synonym_t "synonym_t" { + int replaceant; + int replacement; +} + + +RECORD seg_manager_t "seg_manager_t" { + int_hash_map_tp id_seg_map; + mem_obj_ptr heap[DYNAMIC heap_size]; + int heap_size; + int reserved_id; + int exports_wide; + int sci1_1; + int gc_mark_bits; + size_t mem_allocated; + seg_id_t clones_seg_id; + seg_id_t lists_seg_id; + seg_id_t nodes_seg_id; +} + +RECORD class_t "class_t" { + int script; + reg_t reg; +} + +RECORD sfx_state_t "sfx_state_t" { + songlib_t songlib; +} + +RECORD state_t "state_t" { + int savegame_version; + + string game_version; + sci_version_t version; + menubar_tp menubar; + int status_bar_foreground; + int status_bar_background; + seg_manager_t seg_manager; + int classtable_size; + class_t classtable[DYNAMIC classtable_size]; + sfx_state_t sound; +} + +RECORD local_variables_t "local_variables_t" { + int script_id; + int nr; + reg_t locals[DYNAMIC nr]; +} + +RECORD object_t "object_t" { + int flags; + reg_t pos; + int variables_nr; + int variable_names_nr; + int methods_nr; + reg_t variables[DYNAMIC variables_nr]; +} + +RECORD clone_t "clone_t" { + int flags; + reg_t pos; + int variables_nr; + int variable_names_nr; + int methods_nr; + reg_t variables[DYNAMIC variables_nr]; +} + +RECORD list_t "list_t" { + reg_t first; + reg_t last; +} + +RECORD node_t "node_t" { + reg_t pred; + reg_t succ; + reg_t key; + reg_t value; +} + +RECORD clone_entry_t "clone_entry_t" { + int next_free; + clone_t entry; +} + +RECORD clone_table_t "clone_table_t" { + int entries_nr; + int first_free; + int entries_used; + int max_entry; + clone_entry_t table[DYNAMIC entries_nr]; +} + +RECORD list_entry_t "list_entry_t" { + int next_free; + list_t entry; +} + +RECORD list_table_t "list_table_t" { + int entries_nr; + int first_free; + int entries_used; + int max_entry; + list_entry_t table[DYNAMIC entries_nr]; +} + +RECORD node_entry_t "node_entry_t" { + int next_free; + node_t entry; +} + +RECORD node_table_t "node_table_t" { + int entries_nr; + int first_free; + int entries_used; + int max_entry; + node_entry_t table[DYNAMIC entries_nr]; +} + +RECORD script_t "script_t" { + int nr; + + size_t buf_size; + size_t script_size; + size_t heap_size; + + int_hash_map_tp obj_indices; + int exports_nr; + int synonyms_nr; + int lockers; + int objects_allocated; + int objects_nr; + object_t objects[DYNAMIC objects_allocated]; + + int locals_offset; + int locals_segment; + + int marked_as_deleted; +} + +RECORD sys_string_t "sys_string_t" { + string name; + int max_size; + string value; +} + +RECORD sys_strings_t "sys_strings_t" { + sys_string_t strings[STATIC SYS_STRINGS_MAX]; +} + +RECORD dynmem_t "dynmem_t" { + int size; + string description; + byte buf[DYNAMIC size]; +} + +%END CFSML + +void +write_songlib_t(FILE *fh, songlib_t *songlib) +{ + song_t *seeker = *(songlib->lib); + int songcount = song_lib_count(*songlib); + + fprintf(fh, "{\n"); + fprintf(fh, "songcount = %d\n", songcount); + fprintf(fh, "list = \n"); + fprintf(fh, "[\n"); + while (seeker) + { + seeker->restore_time = seeker->it->get_timepos(seeker->it); + %CFSMLWRITE song_t seeker INTO fh; + seeker = seeker->next; + } + fprintf(fh, "]\n"); + fprintf(fh, "}\n"); +} + +int +read_songlib_t(FILE *fh, songlib_t *songlib, char *lastval, int *line, int *hiteof) +{ + int songcount; + int i; + song_t *newsong; + int oldstatus; + + fscanf(fh, "{\n"); + fscanf(fh, "songcount = %d\n", &songcount); + fscanf(fh, "list = \n"); + fscanf(fh, "[\n"); + *line += 4; + song_lib_init(songlib); + for (i = 0; i < songcount; i++) + { + %CFSMLREAD song_tp &newsong FROM fh ERRVAR *hiteof FIRSTTOKEN lastval LINECOUNTER *line; + song_lib_add(*songlib, newsong); + } + fscanf(fh, "]\n"); + fscanf(fh, "}\n");; + *line += 2; + return 0; +} + +struct { + int type; + char *name; +} mem_obj_string_names[] = { + {MEM_OBJ_INVALID, "INVALID"}, + {MEM_OBJ_SCRIPT, "SCRIPT"}, + {MEM_OBJ_CLONES, "CLONES"}, + {MEM_OBJ_LOCALS, "LOCALS"}, + {MEM_OBJ_STACK, "STACK"}, + {MEM_OBJ_SYS_STRINGS,"SYS_STRINGS"}, + {MEM_OBJ_LISTS,"LISTS"}, + {MEM_OBJ_NODES,"NODES"}, + {MEM_OBJ_HUNK,"HUNK"}, + {MEM_OBJ_DYNMEM,"DYNMEM"}}; + +int +mem_obj_string_to_enum(char *str) +{ + int i; + + for (i = 0; i <= MEM_OBJ_MAX; i++) + { if (!strcasecmp(mem_obj_string_names[i].name, str)) + return i; + } + + return -1; +} + +static int bucket_length; + +void +write_int_hash_map_tp(FILE *fh, int_hash_map_t **foo) +{ + %CFSMLWRITE int_hash_map_t *foo INTO fh; +} + +void +write_song_tp(FILE *fh, song_t **foo) +{ + %CFSMLWRITE song_t *foo INTO fh; +} + +song_iterator_t * +build_iterator(state_t *s, int song_nr, int type, songit_id_t id); + +int +read_song_tp(FILE *fh, song_t **foo, char *lastval, int *line, int *hiteof) +{ + char *token; + int assignment; + *foo = (song_t*) malloc(sizeof(song_t)); + token = _cfsml_get_identifier(fh, line, hiteof, &assignment); + %CFSMLREAD song_t (*foo) FROM fh ERRVAR *hiteof FIRSTTOKEN token LINECOUNTER *line; + (*foo)->delay = 0; + (*foo)->it = NULL; + (*foo)->next_playing = (*foo)->next_stopping = (*foo)->next = NULL; + return 0; +} +int +read_int_hash_map_tp(FILE *fh, int_hash_map_t **foo, char *lastval, int *line, int *hiteof) +{ + *foo = (int_hash_map_t*)malloc(sizeof(int_hash_map_t)); + %CFSMLREAD int_hash_map_t (*foo) FROM fh ERRVAR *hiteof FIRSTTOKEN lastval LINECOUNTER *line; + (*foo)->holes = NULL; + return 0; +} + +void +write_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo) +{ + if (!(*foo)) + { + fputs("\\null", fh); + } else + { + fprintf(fh,"[\n%d=>%d\n", (*foo)->name, (*foo)->value); + if ((*foo)->next) + { + %CFSMLWRITE int_hash_map_node_tp &((*foo)->next) INTO fh; + } else fputc('L', fh); + fputs("]", fh); + } +} + +int +read_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo, char *lastval, int *line, int *hiteof) +{ + static char buffer[80]; + + if (lastval[0] == '\\') { + *foo = NULL; /* No hash map node */ + } else { + *foo = (int_hash_map_node_t*)malloc(sizeof(int_hash_map_node_t)); + if (lastval[0] != '[') + { + sciprintf("Expected opening bracket in hash_map_node_t on line %d\n", *line); + return 1; + } + + do { + (*line)++; + fgets(buffer, 80, fh); + if (buffer[0] == 'L') + { + (*foo)->next = NULL; + buffer[0] = buffer[1]; + } /* HACK: deliberately no else clause here */ + if (buffer[0] == ']') + { + break; + } + else if (buffer[0] == '[') + { + if (read_int_hash_map_node_tp(fh, &((*foo)->next), buffer, line, hiteof)) + return 1; + } + else if (sscanf(buffer, "%d=>%d", &((*foo)->name), &((*foo)->value))<2) + { + sciprintf("Error parsing hash_map_node_t on line %d\n", *line); + return 1; + } + } while (1); + } + + return 0; +} + +void +write_menubar_tp(FILE *fh, menubar_t **foo) +{ + if (*foo) { + + %CFSMLWRITE menubar_t (*foo) INTO fh; + + } else { /* Nothing to write */ + fputs("\\null\\", fh); + } +} + + +int +read_menubar_tp(FILE *fh, menubar_t **foo, char *lastval, int *line, int *hiteof) +{ + + if (lastval[0] == '\\') { + *foo = NULL; /* No menu bar */ + } else { + + *foo = (menubar_t *) sci_malloc(sizeof(menubar_t)); + %CFSMLREAD menubar_t (*foo) FROM fh ERRVAR *hiteof FIRSTTOKEN lastval LINECOUNTER *line; + + } + return *hiteof; +} + +void +write_mem_obj_t(FILE *fh, mem_obj_t *foo) +{ + fprintf(fh, "%s\n", mem_obj_string_names[foo->type].name); + %CFSMLWRITE int &foo->segmgr_id INTO fh; + switch (foo->type) + { + case MEM_OBJ_SCRIPT: + %CFSMLWRITE script_t &foo->data.script INTO fh; + break; + case MEM_OBJ_CLONES: + %CFSMLWRITE clone_table_t &foo->data.clones INTO fh; + break; + case MEM_OBJ_LOCALS: + %CFSMLWRITE local_variables_t &foo->data.locals INTO fh; + break; + case MEM_OBJ_SYS_STRINGS: + %CFSMLWRITE sys_strings_t &foo->data.sys_strings INTO fh; + break; + case MEM_OBJ_STACK: + %CFSMLWRITE int &foo->data.stack.nr INTO fh; + break; + case MEM_OBJ_HUNK: + break; + case MEM_OBJ_LISTS: + %CFSMLWRITE list_table_t &foo->data.lists INTO fh; + break; + case MEM_OBJ_NODES: + %CFSMLWRITE node_table_t &foo->data.nodes INTO fh; + break; + case MEM_OBJ_DYNMEM: + %CFSMLWRITE dynmem_t &foo->data.dynmem INTO fh; + break; + } +} + +int +read_mem_obj_t(FILE *fh, mem_obj_t *foo, char *lastval, int *line, int *hiteof) +{ + char buffer[80]; + foo->type = mem_obj_string_to_enum(lastval); + if (foo->type < 0) + { + sciprintf("Unknown mem_obj_t type %s on line %d\n", lastval, *line); + return 1; + } + + %CFSMLREAD int &foo->segmgr_id FROM fh ERRVAR *hiteof LINECOUNTER *line; + switch (foo->type) + { + case MEM_OBJ_SCRIPT: + %CFSMLREAD script_t &foo->data.script FROM fh ERRVAR *hiteof LINECOUNTER *line; + break; + case MEM_OBJ_CLONES: + %CFSMLREAD clone_table_t &foo->data.clones FROM fh ERRVAR *hiteof LINECOUNTER *line; + break; + case MEM_OBJ_LOCALS: + %CFSMLREAD local_variables_t &foo->data.locals FROM fh ERRVAR *hiteof LINECOUNTER *line; + break; + case MEM_OBJ_SYS_STRINGS: + %CFSMLREAD sys_strings_t &foo->data.sys_strings FROM fh ERRVAR *hiteof LINECOUNTER *line; + break; + case MEM_OBJ_LISTS: + %CFSMLREAD list_table_t &foo->data.lists FROM fh ERRVAR *hiteof LINECOUNTER *line; + break; + case MEM_OBJ_NODES: + %CFSMLREAD node_table_t &foo->data.nodes FROM fh ERRVAR *hiteof LINECOUNTER *line; + break; + case MEM_OBJ_STACK: + %CFSMLREAD int &foo->data.stack.nr FROM fh ERRVAR *hiteof LINECOUNTER *line; + foo->data.stack.entries = (reg_t *)sci_calloc(foo->data.stack.nr, sizeof(reg_t)); + break; + case MEM_OBJ_HUNK: + init_hunk_table(&foo->data.hunks); + break; + case MEM_OBJ_DYNMEM: + %CFSMLREAD dynmem_t &foo->data.dynmem FROM fh ERRVAR *hiteof LINECOUNTER *line; + break; + } + + return *hiteof; +} + +void +write_mem_obj_tp(FILE *fh, mem_obj_t **foo) +{ + if (*foo) { + + %CFSMLWRITE mem_obj_t (*foo) INTO fh; + + } else { /* Nothing to write */ + fputs("\\null\\", fh); + } +} + +int +read_mem_obj_tp(FILE *fh, mem_obj_t **foo, char *lastval, int *line, int *hiteof) +{ + + if (lastval[0] == '\\') { + *foo = NULL; /* No menu bar */ + } else { + *foo = (mem_obj_t *) sci_malloc(sizeof(mem_obj_t)); + %CFSMLREAD mem_obj_t (*foo) FROM fh ERRVAR *hiteof FIRSTTOKEN lastval LINECOUNTER *line; + return *hiteof; + } + return 0; +} + + + +/* This function is called to undo some strange stuff done in preparation +** to writing a gamestate to disk +*/ +void +_gamestate_unfrob(state_t *s) +{ +} + + +int +gamestate_save(state_t *s, char *dirname) +{ + FILE *fh; + sci_dir_t dir; + char *filename; + int fd; + + _global_save_state = s; + s->savegame_version = FREESCI_CURRENT_SAVEGAME_VERSION; + s->dyn_views_list_serial = (s->dyn_views)? s->dyn_views->serial : -2; + s->drop_views_list_serial = (s->drop_views)? s->drop_views->serial : -2; + s->port_serial = (s->port)? s->port->serial : -2; + + if (s->execution_stack_base) { + sciprintf("Cannot save from below kernel function\n"); + return 1; + } + + scimkdir (dirname, 0700); + + if (chdir (dirname)) { + sciprintf("Could not enter directory '%s'\n", dirname); + return 1; + } + + sci_init_dir(&dir); + filename = sci_find_first(&dir, "*"); + while (filename) { + if (strcmp(filename, "..") && strcmp(filename, ".")) + unlink(filename); /* Delete all files in directory */ + filename = sci_find_next(&dir); + } + sci_finish_find(&dir); + +/* + if (s->sound_server) { + if ((s->sound_server->save)(s, dirname)) { + sciprintf("Saving failed for the sound subsystem\n"); + chdir (".."); + return 1; + } + } +*/ + fh = fopen("state", "w" FO_TEXT); + + /* Calculate the time spent with this game */ + s->game_time = time(NULL) - s->game_start_time.tv_sec; + +SCI_MEMTEST; + %CFSMLWRITE state_t s INTO fh; +SCI_MEMTEST; + + fclose(fh); + + _gamestate_unfrob(s); + + + chdir (".."); + return 0; +} + +static seg_id_t +find_unique_seg_by_type(seg_manager_t *self, int type) +{ + int i; + + for (i = 0; i < self->heap_size; i++) + if (self->heap[i] && + self->heap[i]->type == type) + return i; + return -1; +} + +static byte * +find_unique_script_block(state_t *s, byte *buf, int type) +{ + int magic_pos_adder = s->version >= SCI_VERSION_FTU_NEW_SCRIPT_HEADER ? 0 : 2; + + buf += magic_pos_adder; + do { + int seeker_type = getUInt16(buf); + int seeker_size; + + if (seeker_type == 0) break; + if (seeker_type == type) return buf; + + seeker_size = getUInt16(buf + 2); + buf += seeker_size; + } while(1); + + return NULL; +} + +static +void reconstruct_stack(state_t *retval) +{ + seg_id_t stack_seg = find_unique_seg_by_type(&retval->seg_manager, MEM_OBJ_STACK); + dstack_t *stack = &(retval->seg_manager.heap[stack_seg]->data.stack); + + retval->stack_segment = stack_seg; + retval->stack_base = stack->entries; + retval->stack_top = retval->stack_base + VM_STACK_SIZE; +} + +static +int clone_entry_used(clone_table_t *table, int n) +{ + int backup; + int seeker = table->first_free; + clone_entry_t *entries = table->table; + + if (seeker == HEAPENTRY_INVALID) return 1; + + do { + if (seeker == n) return 0; + backup = seeker; + seeker = entries[seeker].next_free; + } while (entries[backup].next_free != HEAPENTRY_INVALID); + + return 1; +} + +static +void load_script(state_t *s, seg_id_t seg) +{ + resource_t *script, *heap; + script_t *scr = &(s->seg_manager.heap[seg]->data.script); + + scr->buf = (byte *) malloc(scr->buf_size); + + script = scir_find_resource(s->resmgr, sci_script, scr->nr, 0); + if (s->version >= SCI_VERSION(1,001,000)) + heap = scir_find_resource(s->resmgr, sci_heap, scr->nr, 0); + + switch (s->seg_manager.sci1_1) + { + case 0 : + sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, seg, SEG_ID); + break; + case 1 : + sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, seg, SEG_ID); + sm_mcpy_in_out( &s->seg_manager, scr->script_size, heap->data, heap->size, seg, SEG_ID); + break; + } +} + +static +void reconstruct_scripts(state_t *s, seg_manager_t *self) +{ + int i; + mem_obj_t *mobj; + object_t **objects; + int *objects_nr; + for (i = 0; i < self->heap_size; i++) + if (self->heap[i]) + { + mobj = self->heap[i]; + switch (mobj->type) + { + case MEM_OBJ_SCRIPT: + { + int j; + script_t *scr = &mobj->data.script; + + load_script(s, i); + scr->locals_block = scr->locals_segment == 0 ? NULL : + &s->seg_manager.heap[scr->locals_segment]->data.locals; + scr->export_table = (guint16 *) find_unique_script_block(s, scr->buf, sci_obj_exports); + scr->synonyms = find_unique_script_block(s, scr->buf, sci_obj_synonyms); + scr->code = NULL; + scr->code_blocks_nr = 0; + scr->code_blocks_allocated = 0; + + if (!self->sci1_1) + scr->export_table += 3; + + for (j = 0; j < scr->objects_nr; j++) + { + byte *data = scr->buf + scr->objects[j].pos.offset; + scr->objects[j].base = scr->buf; + scr->objects[j].base_obj = data; + } + + } + } + } + + for (i = 0; i < self->heap_size; i++) + if (self->heap[i]) + { + mobj = self->heap[i]; + switch (mobj->type) + { + case MEM_OBJ_SCRIPT: + { + int j; + script_t *scr = &mobj->data.script; + + for (j = 0; j < scr->objects_nr; j++) + { + byte *data = scr->buf + scr->objects[j].pos.offset; + + if (self->sci1_1) + { + guint16 *funct_area = (guint16 *) (scr->buf + getUInt16( data + 6 )); + guint16 *prop_area = (guint16 *) (scr->buf + getUInt16( data + 4 )); + + scr->objects[j].base_method = funct_area; + scr->objects[j].base_vars = prop_area; + } else + { + int funct_area = getUInt16( data + SCRIPT_FUNCTAREAPTR_OFFSET ); + object_t *base_obj; + + base_obj = obj_get(s, scr->objects[j].variables[SCRIPT_SPECIES_SELECTOR]); + + if (!base_obj) + { + sciprintf("Object without a base class: Script %d, index %d (reg address "PREG"\n", + scr->nr, j, PRINT_REG(scr->objects[j].variables[SCRIPT_SPECIES_SELECTOR])); + continue; + } + scr->objects[j].variable_names_nr = base_obj->variables_nr; + scr->objects[j].base_obj = base_obj->base_obj; + + scr->objects[j].base_method = (guint16 *) (data + funct_area); + scr->objects[j].base_vars = (guint16 *) (data + scr->objects[j].variable_names_nr * 2 + SCRIPT_SELECTOR_OFFSET); + } + } + } + } + } +} + +void +reconstruct_clones(state_t *s, seg_manager_t *self) +{ + int i; + mem_obj_t *mobj; + + for (i = 0; i < self->heap_size; i++) + if (self->heap[i]) + { + mobj = self->heap[i]; + switch (mobj->type) + { + case MEM_OBJ_CLONES: + { + int j; + clone_entry_t *seeker = mobj->data.clones.table; + + sciprintf("Free list: "); + for (j = mobj->data.clones.first_free; + j != HEAPENTRY_INVALID; + j = mobj->data.clones.table[j].next_free) + { + sciprintf("%d ", j); + } + sciprintf("\n"); + + sciprintf("Entries w/zero vars: "); + for (j = 0; j < mobj->data.clones.max_entry; j++) + { + if (mobj->data.clones.table[j].entry.variables == NULL) + sciprintf("%d ", j); + } + sciprintf("\n"); + + for (j = 0; j < mobj->data.clones.max_entry; j++) + { + object_t *base_obj; + + if (!clone_entry_used(&mobj->data.clones, j)) { + seeker++; + continue; + } + base_obj = obj_get(s, seeker->entry.variables[SCRIPT_SPECIES_SELECTOR]); + if (!base_obj) + { + sciprintf("Clone entry without a base class: %d\n", j); + seeker->entry.base = seeker->entry.base_obj = NULL; + seeker->entry.base_vars = seeker->entry.base_method = NULL; + continue; + } + seeker->entry.base = base_obj->base; + seeker->entry.base_obj = base_obj->base_obj; + seeker->entry.base_vars = base_obj->base_vars; + seeker->entry.base_method = base_obj->base_method; + + seeker++; + } + + break; + } + } + } +} + +int +_reset_graphics_input(state_t *s); + +song_iterator_t * +new_fast_forward_iterator(song_iterator_t *it, int delta); + +static +void reconstruct_sounds(state_t *s) +{ + song_t *seeker; + int it_type = s->resmgr->sci_version >= SCI_VERSION_01 ? + SCI_SONG_ITERATOR_TYPE_SCI1 + : SCI_SONG_ITERATOR_TYPE_SCI0; + + if (s->sound.songlib.lib) + seeker = *(s->sound.songlib.lib); + else + { + song_lib_init(&s->sound.songlib); + seeker = NULL; + } + while (seeker) + { + song_iterator_t *base, *ff; + int oldstatus; + song_iterator_message_t msg; + + base = ff = build_iterator(s, seeker->resource_num, it_type, seeker->handle); + if (seeker->restore_behavior == RESTORE_BEHAVIOR_CONTINUE) + ff = (song_iterator_t *) new_fast_forward_iterator(base, seeker->restore_time); + ff->init(ff); + + msg = songit_make_message(seeker->handle, SIMSG_SET_LOOPS(seeker->loops)); + songit_handle_message(&ff, msg); + msg = songit_make_message(seeker->handle, SIMSG_SET_HOLD(seeker->hold)); + songit_handle_message(&ff, msg); + + + oldstatus = seeker->status; + seeker->status = SOUND_STATUS_STOPPED; + seeker->it = ff; + sfx_song_set_status(&s->sound, seeker->handle, oldstatus); + seeker = seeker->next; + } + +} + +state_t * +gamestate_restore(state_t *s, char *dirname) +{ + FILE *fh; + int fd; + int i; + int read_eof = 0; + state_t *retval; + songlib_t temp; + + if (chdir (dirname)) { + sciprintf("Game state '%s' does not exist\n", dirname); + return NULL; + } + +/* + if (s->sound_server) { + if ((s->sound_server->restore)(s, dirname)) { + sciprintf("Restoring failed for the sound subsystem\n"); + return NULL; + } + } +*/ + + retval = (state_t *) sci_malloc(sizeof(state_t)); + + memset(retval, 0, sizeof(state_t)); + + retval->savegame_version = -1; + _global_save_state = retval; + retval->gfx_state = s->gfx_state; + + fh = fopen("state", "r" FO_TEXT); + if (!fh) { + free(retval); + return NULL; + } + + /* Backwards compatibility settings */ + retval->dyn_views = NULL; + retval->drop_views = NULL; + retval->port = NULL; + retval->save_dir_copy_buf = NULL; + + retval->sound_mute = s->sound_mute; + retval->sound_volume = s->sound_volume; + + %CFSMLREAD-ATOMIC state_t retval FROM fh ERRVAR read_eof; + + fclose(fh); + + if ((retval->savegame_version < FREESCI_MINIMUM_SAVEGAME_VERSION) || + (retval->savegame_version > FREESCI_CURRENT_SAVEGAME_VERSION)) { + + if (retval->savegame_version < FREESCI_MINIMUM_SAVEGAME_VERSION) + sciprintf("Old savegame version detected- can't load\n"); + else + sciprintf("Savegame version is %d- maximum supported is %0d\n", retval->savegame_version, FREESCI_CURRENT_SAVEGAME_VERSION); + + chdir(".."); + free(retval); + return NULL; + } + + sfx_exit(&s->sound); + _gamestate_unfrob(retval); + + /* Set exec stack base to zero */ + retval->execution_stack_base = 0; + retval->execution_stack_pos = 0; + + /* Now copy all current state information */ + /* Graphics and input state: */ + retval->animation_delay = s->animation_delay; + retval->animation_granularity = s->animation_granularity; + retval->gfx_state = s->gfx_state; + + retval->resmgr = s->resmgr; + + temp = retval->sound.songlib; + sfx_init(&retval->sound, retval->resmgr, s->sfx_init_flags); + retval->sfx_init_flags = s->sfx_init_flags; + song_lib_free(retval->sound.songlib); + retval->sound.songlib = temp; + + _reset_graphics_input(retval); + reconstruct_stack(retval); + reconstruct_scripts(retval, &retval->seg_manager); + reconstruct_clones(retval, &retval->seg_manager); + retval->game_obj = s->game_obj; + retval->script_000 = &retval->seg_manager.heap[script_get_segment(s, 0, SCRIPT_GET_DONT_LOAD)]->data.script; + retval->gc_countdown = GC_INTERVAL - 1; + retval->save_dir_copy = make_reg(s->sys_strings_segment, SYS_STRING_SAVEDIR); + retval->save_dir_edit_offset = 0; + retval->sys_strings_segment = find_unique_seg_by_type(&retval->seg_manager, MEM_OBJ_SYS_STRINGS); + retval->sys_strings = &(((mem_obj_t *)(GET_SEGMENT(retval->seg_manager, retval->sys_strings_segment, MEM_OBJ_SYS_STRINGS)))->data.sys_strings); + sys_strings_restore(retval->sys_strings, + s->sys_strings); + + /* Time state: */ + sci_get_current_time(&(retval->last_wait_time)); + retval->game_start_time.tv_sec = time(NULL) - retval->game_time; + retval->game_start_time.tv_usec = 0; + + /* File IO state: */ + retval->file_handles_nr = 2; + retval->file_handles = (FILE **)sci_calloc(2, sizeof(FILE *)); + + /* static parser information: */ + retval->parser_rules = s->parser_rules; + retval->parser_words_nr = s->parser_words_nr; + retval->parser_words = s->parser_words; + retval->parser_suffices_nr = s->parser_suffices_nr; + retval->parser_suffices = s->parser_suffices; + retval->parser_branches_nr = s->parser_branches_nr; + retval->parser_branches = s->parser_branches; + + /* static VM/Kernel information: */ + retval->selector_names_nr = s->selector_names_nr; + retval->selector_names = s->selector_names; + retval->kernel_names_nr = s->kernel_names_nr; + retval->kernel_names = s->kernel_names; + retval->kfunct_table = s->kfunct_table; + retval->kfunct_nr = s->kfunct_nr; + retval->opcodes = s->opcodes; + + memcpy(&(retval->selector_map), &(s->selector_map), sizeof(selector_map_t)); + + retval->max_version = retval->version; + retval->min_version = retval->version; + retval->parser_base = make_reg(s->sys_strings_segment, SYS_STRING_PARSER_BASE); + + /* Copy breakpoint information from current game instance */ + retval->have_bp = s->have_bp; + retval->bp_list = s->bp_list; + + retval->debug_mode = s->debug_mode; + + retval->resource_dir = s->resource_dir; + retval->work_dir = s->work_dir; + retval->kernel_opt_flags = 0; + retval->have_mouse_flag = s->have_mouse_flag; + + retval->successor = NULL; + retval->pic_priority_table = (int*)gfxop_get_pic_metainfo(retval->gfx_state); + retval->game_name = sci_strdup(obj_get_name(retval, retval->game_obj)); + + retval->sound.it = NULL; + retval->sound.flags = s->sound.flags; + retval->sound.song = NULL; + retval->sound.suspended = s->sound.suspended; + retval->sound.debug = s->sound.debug; + reconstruct_sounds(retval); + + chdir (".."); + + return retval; +} diff --git a/engines/sci/engine/sci_graphics.h b/engines/sci/engine/sci_graphics.h new file mode 100644 index 0000000000..74a26b0490 --- /dev/null +++ b/engines/sci/engine/sci_graphics.h @@ -0,0 +1,44 @@ +/*************************************************************************** + sci_graphics.h Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +/* SCI constants and definitions */ + +#ifndef _SCI_GRAPHICS_H_ +#define _SCI_GRAPHICS_H_ + +#define MAX_TEXT_WIDTH_MAGIC_VALUE 192 +/* This is the real width of a text with a specified width of 0 */ + +#define SELECTOR_STATE_SELECTABLE 1 +#define SELECTOR_STATE_FRAMED 2 +#define SELECTOR_STATE_DISABLED 4 +#define SELECTOR_STATE_SELECTED 8 +/* Internal states */ +#define SELECTOR_STATE_DITHER_FRAMED 0x1000 + + +#endif /* !_SCI_GRAPHICS_H_ */ diff --git a/engines/sci/engine/scriptconsole.c b/engines/sci/engine/scriptconsole.c new file mode 100644 index 0000000000..661fa253e9 --- /dev/null +++ b/engines/sci/engine/scriptconsole.c @@ -0,0 +1,1342 @@ +/*************************************************************************** + console.c Copyright (C) 1999..2002 Christoph Reichenbach, TU Darmstadt + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + +***************************************************************************/ +/* Second half of the console implementation: VM dependent stuff */ +/* Remember, it doesn't have to be fast. */ + +#include +#include +#ifdef SCI_CONSOLE + +state_t *con_gamestate = NULL; + +/***************************************************************************/ +/* console commands */ + +static int c_version(struct _state *s); /* displays the package and version number */ +static int c_list(struct _state *s); /* lists various types of things */ +static int c_man(struct _state *s); /* 'manual page' */ +static int c_set(struct _state *s); /* sets an int variable */ +static int c_print(struct _state *s); /* prints a variable */ +static int c_size(struct _state *s); /* displays the size of a resource */ +static int c_dump(struct _state *s); /* gives a hex dump of a resource */ +static int c_objinfo(struct _state *s); /* shows some info about one class */ +static int c_objmethods(struct _state *s); /* Disassembles all methods of a class */ +static int c_hexgrep(struct _state *s); /* Searches a string in one resource or resource class */ +static int c_selectornames(struct _state *s); /* Displays all selector names */ +static int c_kernelnames(struct _state *s); /* Displays all kernel function names */ +static int c_dissectscript(struct _state *s); /* Splits a script into objects and explains them */ + +typedef struct { + const char *name; + const char *description; +} cmd_mm_entry_t; /* All later structures must "extend" this */ + +typedef cmd_mm_entry_t cmd_page_t; /* Simple info page */ + +typedef struct { + const char *name; + const char *description; + int (*command) (state_t *); + const char *param; +} cmd_command_t; + +typedef struct +{ + const char *name; + const char *description; + union { + int *intp; + char **charpp; + reg_t *reg; + } var; +} cmd_var_t; + + +typedef void printfunc_t(cmd_mm_entry_t *data, int full); + +typedef struct { + const char *name; + void *data; /* cmd_mm_entry_t */ + size_t size_per_entry; + printfunc_t *print; + int entries; /* Number of used entries */ + int allocated; /* Number of allocated entries */ +} cmd_mm_struct_t; + +#define CMD_MM_ENTRIES 3 /* command console memory and manual page manager */ +#define CMD_MM_DEFAULT_ALLOC 4 /* Number of table entries to allocate per default */ + +#define CMD_MM_CMD 0 /* Commands */ +#define CMD_MM_VAR 1 /* Variables */ +#define CMD_MM_DOC 2 /* Misc. documentation */ + +static const char *cmd_mm_names[CMD_MM_ENTRIES] = { + "Commands", + "Variables", + "Documentation" +}; +static size_t cmd_mm_sizes_per_entry[CMD_MM_ENTRIES] = { + sizeof(cmd_command_t), + sizeof(cmd_var_t), + sizeof(cmd_page_t) +}; + + +static void _cmd_print_command(cmd_mm_entry_t *data, int full); +static void _cmd_print_var(cmd_mm_entry_t *data, int full); +static void _cmd_print_page(cmd_mm_entry_t *data, int full); + +static printfunc_t *cmd_mm_printers[CMD_MM_ENTRIES] = { + _cmd_print_command, + _cmd_print_var, + _cmd_print_page +}; + +static cmd_mm_struct_t cmd_mm[CMD_MM_ENTRIES]; + +static int _cmd_initialized = 0; +static int _lists_need_sorting = 0; + +unsigned int cmd_paramlength; +cmd_param_t *cmd_params; + + +/********** dmalloc functions **********/ + +#ifdef WITH_DMALLOC + +int +c_dm_stats (state_t * s) +{ + dmalloc_log_stats(); + return 0; +} + +int +c_dm_log_unfreed (state_t * s) +{ + dmalloc_log_unfreed(); + return 0; +} + +int +c_dm_verify (state_t * s) +{ + unsigned long pointer_var; + void *ptr; + + pointer_var = strtoul (cmd_params[0].str, NULL, 0); + ptr = (void *) pointer_var; + + dmalloc_verify (ptr); + + return 0; +} + +int +c_dm_debug (state_t * s) +{ + if (cmd_paramlength) { + long newval = strtol (cmd_params[0].str, NULL, 0); + + sciprintf ("Setting dmalloc_debug(%ld)\n", newval); + dmalloc_debug (newval); + } + else + sciprintf ("dmalloc_debug is at 0x%lx\n", dmalloc_debug_current()); + return 0; +} + +int +c_dm_mark (state_t * s) +{ + unsigned long mark = dmalloc_mark(); + + dmalloc_message ("------------- MARK 0x%lx ---------------\n", mark); + sciprintf ("mark 0x%lx\n", mark); + return 0; +} + +int +c_dm_chmark (state_t * s) +{ + unsigned long mark = strtoul (cmd_params[0].str, NULL, 0); + sciprintf ("Checking mark 0x%lx\n", mark); + dmalloc_message ("--- Mark 0x%lx:\n", mark); + dmalloc_log_changed (mark, 1, 1, 1); + return 0; +} + +int +c_dm_print (state_t * s) +{ + int i; + for (i = 0; i < cmd_paramlength; i++) + dmalloc_message ("%s\n", cmd_params[i].str); + return 0; +} + +void +con_init_dmalloc() +{ + con_hook_command (c_dm_stats, "dm_stats", "", + "Prints memory usage stats\n to the dmalloc output file\n\n dm_stats"); + con_hook_command (c_dm_log_unfreed, "dm_log_unfreed", "", + "Prints unfreed pointer\n information to the dmalloc\n output file\n\n" + "USAGE\n\n dm_log_unfreed"); + con_hook_command (c_dm_verify, "dm_verify", "s", + "Verifies one pointer,\n prints output to dmalloc file\n\nUSAGE\n\n" + " dm_verify \n dm_verify 0\n\n 'dm_verify 0' will verify\n ALL current pointers.\n"); + con_hook_command (c_dm_debug, "dm_debug", "s*", + "Sets the dmalloc debug\n state or displays it\n\nUSAGE\n\n dm_debug \n dm_debug"); + con_hook_command (c_dm_mark, "dm_mark", "", + "Gets a mark describing\n the current heap state\n\nUSAGE\n\n dm_mark\n\n" + " The mark is written to the\n dmalloc output file and\n to sci output.\n\nSEE ALSO\n\n cm_chmark"); + con_hook_command (c_dm_chmark, "dm_chmark", "s", + "Checks changes in the\n heap state since a certain\n mark was retreived\n\n" + "USAGE\n\n c_dm_chmark \n\n Output is written to the\n dmalloc output file.\n\n Use dm_mark to retreive a\n" + " mark.\n\nSEE ALSO\n\n c_dm_mark"); + con_hook_command (c_dm_print, "dm_print", "s*", + "Prints something to the\n dmalloc output.\n\nUSAGE\n\n dm_print "); +} +#else /* WITH_DMALLOC */ + +void +con_init_dmalloc (void) +{ +} + +#endif /* WITH_DMALLOC */ + + +void +_cmd_exit (void) +{ + int t; + + for (t = 0; t < CMD_MM_ENTRIES; t++) + free(cmd_mm[t].data); +} + +static cmd_mm_entry_t * +cmd_mm_find(char *name, int type) +{ + int i; + + for (i = 0; i < cmd_mm[type].entries; i++) + if (!strcmp(((cmd_mm_entry_t *)((byte *)cmd_mm[type].data + i * cmd_mm[type].size_per_entry))->name, name)) + return ((cmd_mm_entry_t *)((byte *)cmd_mm[type].data + i * cmd_mm[type].size_per_entry)); + + return NULL; +} + +static int +_cmd_mm_comp (const void *a, const void *b) +{ + return strcmp (((cmd_mm_entry_t *) a)->name, ((cmd_mm_entry_t *) b)->name); +} + +void +con_sort_all (void) +{ + int i; + + for (i = 0; i < CMD_MM_ENTRIES; i++) + if (cmd_mm[i].entries && _lists_need_sorting & (1 << i)) + qsort (cmd_mm[i].data, cmd_mm[i].entries, cmd_mm[i].size_per_entry, + _cmd_mm_comp); + + _lists_need_sorting = 0; +} + +void +con_init (void) +{ + if (!_cmd_initialized) { + int i; + + _cmd_initialized = 1; + for (i = 0; i < CMD_MM_ENTRIES; i++) { + cmd_mm[i].name = cmd_mm_names[i]; + cmd_mm[i].size_per_entry = cmd_mm_sizes_per_entry[i]; + cmd_mm[i].entries = 0; + cmd_mm[i].allocated = CMD_MM_DEFAULT_ALLOC; + cmd_mm[i].data = sci_calloc(cmd_mm[i].allocated, cmd_mm[i].size_per_entry); + cmd_mm[i].print = cmd_mm_printers[i]; + } + + atexit (_cmd_exit); + + /* Hook up some commands */ + con_hook_command (&c_version, "version", "", + "Displays the version number"); + con_hook_command (&c_list, "list", "s*", + "Lists various things (try 'list')"); + con_hook_command (&c_man, "man", "s", + "Gives a short description of something"); + con_hook_command (&c_print, "print", "s", "Prints an int variable"); + con_hook_command (&c_set, "set", "si", "Sets an int variable"); + con_hook_command (&c_size, "size", "si", + "Displays the size of a resource"); + con_hook_command (&c_dump, "dump", "si", "HexDumps a resource"); + con_hook_command (&c_hexgrep, "hexgrep", "shh*", + "Searches some resources for a\n" + " particular sequence of bytes, re-\n presented" + " as hexadecimal numbers.\n\n" + "EXAMPLES:\n hexgrep script e8 03 c8 00\n" + " hexgrep pic.042 fe"); + con_hook_command (&c_dissectscript, "dissectscript", "i", + "Examines a script."); + + con_hook_page("addresses", + "Passing address parameters\n\n" + " Address parameters may be passed in one of\n" + " three forms:\n" + " - ssss:oooo -- where 'ssss' denotes a\n" + " segment and 'oooo' an offset. Example:\n" + " \"a:c5\" would address something in seg-\n" + " ment 0xa at offset 0xc5.\n" + " - &scr:oooo -- where 'scr' is a script number\n" + " and oooo an offset within that script; will\n" + " fail if the script is not currently loaded\n" + " - $REG -- where 'REG' is one of 'PC', 'ACC',\n" + " 'PREV' or 'OBJ': References the address\n" + " indicated by the register of this name.\n" + " - $REG+n (or -n) -- Like $REG, but modifies\n" + " the offset part by a specific amount (which\n" + " is specified in hexadecimal).\n" + " - ?obj -- Looks up an object with the specified\n" + " name, uses its address. This will abort if\n" + " the object name is ambiguous; in that case,\n" + " a list of addresses and indices is provided.\n" + " ?obj.idx may be used to disambiguate 'obj'\n" + " by the index 'idx'.\n"); + + con_init_dmalloc(); + + con_hook_int (&con_passthrough, "con_passthrough", + "scicon->stdout passthrough"); + } +} + +static inline int +clone_is_used(clone_table_t *t, int idx) +{ + return ENTRY_IS_VALID(t, idx); +} + +int +parse_reg_t(state_t *s, char *str, reg_t *dest) +{ /* Returns 0 on success */ + int rel_offsetting = 0; + char *offsetting = NULL; + /* Non-NULL: Parse end of string for relative offsets */ + char *endptr; + + if (!s) { + sciprintf("Addresses can only be parsed if a global state is present"); + return 1; /* Requires a valid state */ + } + + if (*str == '$') { /* Register */ + rel_offsetting = 1; + + if (!strncasecmp(str+1, "PC", 2)) { + *dest = s->execution_stack[s->execution_stack_pos].addr.pc; + offsetting = str + 3; + } else if (!strncasecmp(str+1, "P", 1)) { + *dest = s->execution_stack[s->execution_stack_pos].addr.pc; + offsetting = str + 2; + } else if (!strncasecmp(str+1, "PREV", 4)) { + *dest = s->r_prev; + offsetting = str + 5; + } else if (!strncasecmp(str+1, "ACC", 3)) { + *dest = s->r_acc; + offsetting = str + 4; + } else if (!strncasecmp(str+1, "A", 1)) { + *dest = s->r_acc; + offsetting = str + 2; + } else if (!strncasecmp(str+1, "OBJ", 3)) { + *dest = s->execution_stack[s->execution_stack_pos].objp; + offsetting = str + 4; + } else if (!strncasecmp(str+1, "O", 1)) { + *dest = s->execution_stack[s->execution_stack_pos].objp; + offsetting = str + 2; + } else return 1; /* No matching register */ + + if (!*offsetting) + offsetting = NULL; + else if (*offsetting != '+' && *offsetting != '-') + return 1; + } else if (*str == '&') { + int script_nr; + /* Look up by script ID */ + char *colon = strchr(str, ':'); + + if (!colon) + return 1; + *colon = 0; + offsetting = colon+1; + + script_nr = strtol(str+1, &endptr, 10); + + if (*endptr) + return 1; + + dest->segment = sm_seg_get(&s->seg_manager, script_nr); + + if (!dest->segment) { + return 1; + } + } else if (*str == '?') { + int index = -1; + int times_found = 0; + char *str_objname; + char *str_suffix; + char suffchar = 0; /* Supress spurious -Wall warning */ + int i; + /* Parse obj by name */ + + str_objname = strchr(str, '+'); + str_suffix = strchr(str, '-'); + if (str_objname < str_suffix) + str_suffix = str_objname; + if (str_suffix) { + suffchar = (*str_suffix); + *str_suffix = 0; + } + + str_objname = strchr(str, '.'); + + if (str_objname) { + *str_objname = 0; + index = strtol(str_objname+1, &endptr, 16); + if (*endptr) + return -1; + } + + str_objname = str + 1; + + /* Now all values are available; iterate over all objects. */ + for (i = 0; i < s->seg_manager.heap_size; i++) { + mem_obj_t *mobj = s->seg_manager.heap[i]; + int idx = 0; + int max_index = 0; + + if (mobj) { + if (mobj->type == MEM_OBJ_SCRIPT) + max_index = mobj->data.script.objects_nr; + else if (mobj->type == MEM_OBJ_CLONES) + max_index = mobj->data.clones.max_entry; + } + + while (idx < max_index) { + int valid = 1; + object_t *obj = NULL; /* Surpress spurious warning */ + reg_t objpos; + objpos.segment = i; + + if (mobj->type == MEM_OBJ_SCRIPT) { + obj = mobj->data.script.objects + idx; + objpos.offset = obj->pos.offset; + } else if (mobj->type == MEM_OBJ_CLONES) { + obj = &(mobj->data.clones.table[idx].entry); + objpos.offset = idx; + valid = clone_is_used(&mobj->data.clones, idx); + } + + if (valid) { + char *objname = (char *) obj->base + + obj->variables[SCRIPT_NAME_SELECTOR].offset; + if (!strcmp(objname, str_objname)) { + /* Found a match! */ + if (index < 0 || + times_found == index) + *dest = objpos; + else if (times_found < 0 && index) { + + if (index == 1) { + /* First time we realized + ** the ambiguity */ + sciprintf("Ambiguous:\n"); + sciprintf(" %3x: ["PREG"] %s\n", 0, PRINT_REG(*dest), str_objname); + } + sciprintf(" %3x: ["PREG"] %s\n", index, PRINT_REG(objpos), str_objname); + } + ++times_found; + } + } + + ++idx; + } + + } + + if (!times_found) + return 1; + + if (times_found > 1 + && index < 0) { + sciprintf("Ambiguous: Aborting.\n"); + return 1; /* Ambiguous */ + } + + if (times_found <= index) + return 1; /* Not found */ + + offsetting = str_suffix; + if (offsetting) + *str_suffix = suffchar; + rel_offsetting = 1; + } else { + char *colon = strchr(str, ':'); + + if (!colon) { + offsetting = str; + dest->segment = 0; + } else { + *colon = 0; + offsetting = colon+1; + + dest->segment = strtol(str, &endptr, 16); + if (*endptr) + return 1; + } + } + if (offsetting) { + int val = strtol(offsetting, &endptr, 16); + + if (rel_offsetting) + dest->offset += val; + else + dest->offset = val; + + if (*endptr) + return 1; + } + + return 0; +} + +void +con_parse (state_t *s, const char *command) +{ + int quote = 0; /* quoting? */ + int done = 0; /* are we done yet? */ + int cdone = 0; /* Done with the current command? */ + const char *paramt; /* parameter types */ + char *cmd = (command && command[0]) ? (char *) sci_strdup (command) : + (char *) sci_strdup(" "); + char *_cmd = cmd; + int pos = 0; + + if (!_cmd_initialized) + con_init(); + + while (!done) { + cmd_command_t *command_todo; + int onvar = 1; /* currently working on a variable? */ + unsigned int parammem = 0; + unsigned int i; + cdone = 0; + pos = 0; + + /* cmd_params = sci_realloc(cmd_params, parammem); */ + cmd_paramlength = 0; + + while (*cmd == ' ') + cmd++; + + while (!cdone) { + switch (cmd[pos]) { + case 0: + done = 1; + case ';': + if (!quote) + cdone = 1; + case ' ': + if (!quote) + cmd[pos] = onvar = 0; + break; + case '\\': /* don't check next char for special meaning */ + memmove (cmd + pos, cmd + pos + 1, strlen (cmd + pos) - 1); + break; + case '"': + quote ^= 1; + memmove (cmd + pos, cmd + pos + 1, strlen (cmd + pos)); + pos--; + break; + default: + if (!onvar) { + onvar = 1; + if (cmd_paramlength == parammem) + cmd_params = (cmd_param_t*)sci_realloc(cmd_params, + sizeof (cmd_param_t) + * (parammem += 8)); + + cmd_params[cmd_paramlength].str = cmd + pos; + + cmd_paramlength++; + } + break; + } + pos++; + } + + if (quote) + sciprintf ("unbalanced quotes\n"); + else if (strcmp (cmd, "") != 0) { + + command_todo = (cmd_command_t *) cmd_mm_find(cmd, CMD_MM_CMD); + + if (!command_todo) + sciprintf ("%s: not found\n", cmd); + else { + unsigned int minparams; + int need_state = 0; + + paramt = command_todo->param; + if (command_todo->param[0] == '!') { + need_state = 1; + paramt++; + } + + minparams = strlen (paramt); + + if ((paramt[0] != 0) && (paramt[strlen (paramt) - 1] == '*')) + minparams -= 2; + + if (cmd_paramlength < minparams) + sciprintf ("%s: needs more than %d parameters\n", + cmd, cmd_paramlength); + + else if ((cmd_paramlength > strlen (paramt)) + && ((strlen (paramt) == 0) + || paramt[strlen (paramt) - 1] != '*')) + sciprintf ("%s: too many parameters", cmd); + else { + int do_execute = !need_state || s; /* /me wants an + ** implication arrow */ + char paramtype; + int paramtypepos = 0; + char *endptr; + + for (i = 0; i < cmd_paramlength; i++) { + paramtype = paramt[paramtypepos]; + + if ((paramt[paramtypepos + 1]) + && (paramt[paramtypepos + 1] != '*')) + paramtypepos++; + /* seek next param type unless end of string or '* ' */ + + switch (paramtype) { + /* Now turn the parameters into variables of the appropriate types, + ** unless they're strings, and store them in the global cmd_params[] + ** structure */ + + case 'a': { + char *oldname = cmd_params[i].str; + if (parse_reg_t(s, oldname, + &(cmd_params[i].reg))) { + sciprintf("%s: '%s' is not an address or object\n", cmd, oldname); + do_execute = 0; + } + break; + } + + case 'i': { + char *orgstr = cmd_params[i].str; + + cmd_params[i].val = strtol (orgstr, &endptr, 0); + if (*endptr != '\0') { + do_execute = 0; + sciprintf ("%s: '%s' is not an int\n", cmd, orgstr); + } + } + break; + + case 'h': { + char *orgstr = cmd_params[i].str; + + cmd_params[i].val = strtol (orgstr, &endptr, 16); + + if (*endptr != '\0') { + do_execute = 0; + sciprintf ("%s: '%s' is not a hex number\n", cmd, orgstr); + } + + cmd_params[i].val &= 0xff; /* Clip hex numbers to 0x00 ... 0xff */ + } + break; + + case 's': + break; + + default: + fprintf(stderr, "Internal error: Heap corruption or prior assertion failed:\n" + "Unknown parameter type '%c' for funtion\n", paramtype); + + } + } + + if (do_execute) { + command_todo->command(s); + } else fprintf(stderr, "Skipping command...\n"); + } + } + } + cmd += pos; + } + + free (_cmd); + if (cmd_params) + free (cmd_params); + cmd_params = NULL; + +} + +/* (unused) +static cmd_mm_entry_t * +con_iterate_entry(int ID, int *counter) +{ + byte *retval; + con_init(); + + if (*counter >= cmd_mm[ID].entries) + return 0; + retval = cmd_mm[ID].data; + retval += (*counter) * cmd_mm[ID].size_per_entry; + + (*counter)++; + + return (cmd_mm_entry_t *) retval; +} +*/ + +static cmd_mm_entry_t * +con_alloc_page_entry(int ID) +{ + int entry; + con_init(); + + if (cmd_mm[ID].entries >= cmd_mm[ID].allocated) { + int nextsize = cmd_mm[ID].allocated; + if (nextsize >= 64) + nextsize += 16; + else + nextsize <<= 1; + + cmd_mm[ID].data = sci_realloc(cmd_mm[ID].data, + nextsize * cmd_mm[ID].size_per_entry); + cmd_mm[ID].allocated = nextsize; + } + + _lists_need_sorting |= (1 << ID); + + entry = cmd_mm[ID].entries++; + return (cmd_mm_entry_t *) (((byte *)cmd_mm[ID].data) + + entry * cmd_mm[ID].size_per_entry); +} + +int +con_hook_page(const char *name, const char *body) +{ + cmd_page_t *page = (cmd_page_t *) con_alloc_page_entry(CMD_MM_DOC); + + page->name = name; + page->description = body; + + return 0; +} + +int +con_hook_command (int command (state_t *), const char *name, const char *param, + const char *description) +{ + cmd_command_t *cmd = NULL; + unsigned int i; + + + if (NULL == name) { + sciprintf("console.c: con_hook_command(): NULL passed for name\n"); + return -1; + } + + if (command == NULL) + return 1; + + if (param == NULL) + param = ""; + + if (description == NULL) + description = ""; + + i = 0; + while (param[i] != 0) { + switch (param[i]) { + case '*': + if (param[i + 1] != 0) + return 1; + if (i == 0) + return 1; + case 'h': + case '!': + case 'i': + case 'a': + case 's': + case 'r': + break; + default: + return 1; + } + i++; + } + cmd = (cmd_command_t *) con_alloc_page_entry(CMD_MM_CMD); + + cmd->command = command; + cmd->name = name; + cmd->param = param; + cmd->description = description; + + return 0; +} + + +int +con_hook_int (int *pointer, const char *name, const char *description) +{ + cmd_var_t *var; + + if (pointer == NULL) + return 1; + + if (description == NULL) + description = ""; + + var = (cmd_var_t *) con_alloc_page_entry(CMD_MM_VAR); + + var->var.intp = pointer; + var->name = name; + var->description = description; + + return 0; +} + + +/*************************************************************************** + * Console commands and support functions + ***************************************************************************/ + + +static int +get_resource_number (char *resid) +/* Gets the resource number of a resource string, or returns -1 */ +{ + int i, res = -1; + + for (i = 0; i < sci_invalid_resource; i++) + if (strcmp (sci_resource_types[i], resid) == 0) + res = i; + return res; +} + +static int +c_version (state_t * s) +{ + if (NULL == s) { + sciprintf("console.c: c_version: NULL passed for parameter s\n"); + return -1; + } + + sciprintf ("FreeSCI, version " VERSION "\n"); + sciprintf ("Resource file version: %s\n", sci_version_types[s->resmgr->sci_version]); + sciprintf ("Emulated interpreter version: %d.%03d.%03d\n", + SCI_VERSION_MAJOR(s->version), + SCI_VERSION_MINOR(s->version), + SCI_VERSION_PATCHLEVEL(s->version)); + + return 0; +} + +static int +c_list_words(state_t *s) +{ + word_t **words; + int words_nr; + int i; + + words = vocab_get_words(s->resmgr, &words_nr); + + if (!words) { + sciprintf("No vocabulary.\n"); + return 1; + } + + for (i = 0; i < words_nr; i++) + sciprintf("%4d: %03x [%03x] %s\n", + i, + words[i]->w_class, + words[i]->group, + words[i]->word); + + vocab_free_words(words, words_nr); + return 0; +} + +int +c_list_suffices(state_t *s) +{ + suffix_t **suffices; + int suffices_nr; + int i; + char word_buf[256], alt_buf[256]; + + suffices = vocab_get_suffices(s->resmgr, &suffices_nr); + + if (!suffices) { + sciprintf("No suffix vocabulary.\n"); + return 1; + } + + for (i = 0; i < suffices_nr; i++) { + suffix_t *suf = suffices[i]; + + strncpy(word_buf, suf->word_suffix, + suf->word_suffix_length); + word_buf[suf->word_suffix_length] = 0; + strncpy(alt_buf, suf->alt_suffix, + suf->alt_suffix_length); + alt_buf[suf->alt_suffix_length] = 0; + + sciprintf("%4d: (%03x) -%12s => -%12s (%03x)\n", + i, suf->class_mask, word_buf, + alt_buf, suf->result_class); + } + + vocab_free_suffices(s->resmgr, suffices, suffices_nr); + return 0; +} + +static void +_cmd_print_command(cmd_mm_entry_t *data, int full) +{ + const char *paramseeker = ((cmd_command_t *) data)->param; + + if (full) { + sciprintf ("SYNOPSIS\n\n %s ", data->name, paramseeker); + + while (*paramseeker) { + switch (*paramseeker) { + case '!': break; + case 'i': + sciprintf (" (int)"); + break; + case 'a': + sciprintf (" (addr)"); + break; + case 's': + sciprintf (" (string)"); + break; + case 'h': + sciprintf (" (hexbyte)"); + break; + case '*': + sciprintf ("*"); + break; + default: + sciprintf (" (Unknown(%c))", *paramseeker); + } + paramseeker++; + } + + sciprintf("\n\nDESCRIPTION\n\n %s", + data->description); + } else + sciprintf(" %s", data->name); +} + +static void +_cmd_print_var(cmd_mm_entry_t *data, int full) +{ + cmd_var_t *var = (cmd_var_t *) data; + if (full) + sciprintf ("VALUE\n\n"); + sciprintf(" %s = %d\n", var->name, *(var->var.intp)); + + if (full) + sciprintf("\n\nDESCRIPTION\n\n %s", + data->description); +} + +static void +_cmd_print_page(cmd_mm_entry_t *data, int full) +{ + if (full) + sciprintf("\n\nDESCRIPTION\n\n %s\n", + data->description); + else sciprintf("%s\n", data->name); +} + +static int +c_list (state_t * s) +{ + if (_lists_need_sorting) + con_sort_all(); + + if (cmd_paramlength == 0) + { + sciprintf ("usage: list [type]\nwhere type is one of the following:\n" + "cmds - lists all commands\n" + "vars - lists all variables\n" + "docs - lists all misc. documentation\n" + "\n" + "restypes - lists all resource types\n" + "selectors - lists all selectors\n" + "syscalls - lists all kernel functions\n" + "words - lists all kernel words\n" + "suffixes - lists all suffix replacements\n" + "[resource] - lists all [resource]s"); + } + else if (cmd_paramlength == 1) { + const char *mm_subsects[3] = {"cmds", "vars", "docs"}; + int mm_found = -1; + int i; + + for (i = 0; i < 3; i++) + if (mm_subsects[i] && !strcmp(mm_subsects[i], cmd_params[0].str)) + mm_found = i; + + if (mm_found >= 0) + for (i = 0; i < cmd_mm[mm_found].entries; i++) + cmd_mm[mm_found].print((cmd_mm_entry_t *) + (((byte *)cmd_mm[mm_found].data) + + i * cmd_mm[mm_found].size_per_entry), 0); + else { + if (!s) { + sciprintf("You need a state to do that!\n"); + return 1; + } + + if (!strcmp("selectors", cmd_params[0].str)) + return c_selectornames(s); + else if (!strcmp("syscalls", cmd_params[0].str)) + return c_kernelnames(s); + else if (!strcmp("suffixes", cmd_params[0].str) + || !strcmp("suffices", cmd_params[0].str) + || !strcmp("sufficos", cmd_params[0].str)) + /* sufficos: Accusative Plural of 'suffix' */ + return c_list_suffices(s); + else if (!strcmp("words", cmd_params[0].str)) + return c_list_words(s); + else if (strcmp ("restypes", cmd_params[0].str) == 0) { + int i; + for (i = 0; i < sci_invalid_resource; i++) + sciprintf ("%s\n", sci_resource_types[i]); + } + else { + int res = get_resource_number (cmd_params[0].str); + if (res == -1) + sciprintf ("Unknown resource type: '%s'\n", cmd_params[0].str); + else { + int i; + for (i = 0; i < sci_max_resource_nr[s->resmgr->sci_version]; i++) + if (scir_test_resource (s->resmgr, res, i)) + sciprintf ("%s.%03d\n", sci_resource_types[res], i); + } + } + } + } + else + sciprintf ("list can only be used with one argument"); + return 0; +} + + +static int +c_man (state_t * s) +{ + int section = 0; + unsigned int i; + char *name = cmd_params[0].str; + char *c = strchr(name, '.'); + cmd_mm_entry_t *entry; + + if (c) { + *c = 0; + section = atoi(c + 1); + } + + if (section < 0 || section >= CMD_MM_ENTRIES) { + sciprintf("Invalid section %d\n", + section); + return 1; + } + + sciprintf("section:%d\n", section); + if (section) + entry = cmd_mm_find(name, section - 1); + else + for (i = 0; i < CMD_MM_ENTRIES && !section; i++) { + if ((entry = cmd_mm_find(name, i))) + section = i+1; + } + + if (!entry) { + sciprintf("No manual entry\n"); + return 1; + } + + sciprintf ("-- %s: %s.%d\n", cmd_mm[section - 1].name, name, section); + cmd_mm[section - 1].print(entry, 1); + + return 0; +} + +static int +c_set (state_t * s) +{ + cmd_var_t *var = (cmd_var_t *) cmd_mm_find(cmd_params[0].str, CMD_MM_VAR); + + if (var) + *(var->var.intp) = cmd_params[1].val; + + return 0; +} + +static int +c_print (state_t * s) +{ + cmd_var_t *var = (cmd_var_t *) cmd_mm_find(cmd_params[0].str, CMD_MM_VAR); + + if (var) + sciprintf ("%d", *(var->var.intp)); + else + sciprintf ("Not defined."); + + return 0; +} + + +static int +c_size (state_t * s) +{ + int res = get_resource_number (cmd_params[0].str); + if (res == -1) + sciprintf ("Resource type '%s' is not valid\n", cmd_params[0].str); + else + { + resource_t *resource = scir_find_resource(s->resmgr, res, cmd_params[1].val, 0); + if (resource) + { + sciprintf ("Size: %d\n", resource->size); + } + else + sciprintf ("Resource %s.%03d not found\n", cmd_params[0].str, + cmd_params[1].val); + } + return 0; +} + + +static int +c_dump (state_t * s) +{ + int res = get_resource_number (cmd_params[0].str); + + if (res == -1) + sciprintf ("Resource type '%s' is not valid\n", cmd_params[0].str); + else + { + resource_t *resource = scir_find_resource(s->resmgr, res, cmd_params[1].val, 0); + if (resource) + sci_hexdump (resource->data, resource->size, 0); + else + sciprintf ("Resource %s.%03d not found\n", cmd_params[0].str, + cmd_params[1].val); + } + return 0; +} + + +static int +c_hexgrep (state_t * s) +{ + int i, seeklen, resnr, restype, resmax; + unsigned char *seekstr = NULL; + resource_t *script = NULL; + char *dot = strchr (cmd_params[0].str, '.'); + + if (NULL == s) + { + fprintf(stderr, "console.c: c_hexgrep(): NULL passed for s\r\n"); + return(-1); + } + + seekstr = (unsigned char*)sci_malloc (seeklen = (cmd_paramlength - 1)); + + if (NULL == seekstr) + { + fprintf(stderr, "console.c: c_hexgrep(): malloc failed for seekstr\r\n"); + return(-1); + } + + for (i = 0; i < seeklen; i++) + seekstr[i] = (byte)cmd_params[i + 1].val; + + if (dot) + { + *dot = 0; + resmax = resnr = atoi (dot + 1); + } + else + { + resnr = 0; + resmax = 999; + } + + if ((restype = get_resource_number (cmd_params[0].str)) == -1) + { + sciprintf ("Unknown resource type \"%s\"\n", cmd_params[0].str); + free(seekstr); + return 1; + } + + for (; resnr <= resmax; resnr++) + if ((script = scir_find_resource(s->resmgr, restype, resnr, 0))) + { + int seeker = 0, seekerold = 0; + int comppos = 0; + int output_script_name = 0; + + while (seeker < script->size) + { + + if (script->data[seeker] == seekstr[comppos]) + { + if (comppos == 0) + seekerold = seeker; + + comppos++; + + if (comppos == seeklen) + { + comppos = 0; + seeker = seekerold + 1; + + if (!output_script_name) + { + sciprintf ("\nIn %s.%03d:\n", sci_resource_types[restype], resnr); + output_script_name = 1; + } + sciprintf (" 0x%04x\n", seekerold); + } + } + else + comppos = 0; + + seeker++; + } + } + + free (seekstr); + + return 0; +} + + +static int +c_selectornames (state_t * s) +{ + int namectr; + char **snames = NULL; + int seeker = 0; + + if (NULL == s) + { + sciprintf("console.c: c_selectornames(): NULL passed for parameter s\n"); + return -1; + } + + snames = vocabulary_get_snames (s->resmgr, &namectr, s ? s->version : 0); + + if (!snames) + { + sciprintf ("No selector name table found!\n"); + return 1; + } + + sciprintf ("Selector names in numeric order:\n"); + while (snames[seeker]) + { + sciprintf ("%03x: %s\n", seeker, snames[seeker]); + seeker++; + } + vocabulary_free_snames (snames); + return 0; +} + +static int +c_kernelnames (state_t * s) +{ + int knamectr; + char **knames = vocabulary_get_knames (s->resmgr, &knamectr); + int seeker = 0; + + if (NULL == s) { + sciprintf("console.c: c_kernelnames NULL passed for parameter s\n"); + return -1; + } + + if (!knames) + { + sciprintf ("No kernel name table found!\n"); + return 1; + } + + sciprintf ("Syscalls in numeric order:\n"); + for (seeker = 0; seeker < knamectr; seeker++) + sciprintf ("%03x: %s\n", seeker, knames[seeker]); + + vocabulary_free_knames (knames); + return 0; +} + +static int +c_dissectscript (state_t * s) +{ + if (NULL == s) + { + sciprintf("console.c: c_dissectscript(): NULL passed for parameter s\n"); + return -1; + } + + script_dissect (s->resmgr, cmd_params[0].val, s->selector_names, s->selector_names_nr); + return 0; +} +#endif /* SCI_CONSOLE */ + + diff --git a/engines/sci/engine/scriptdebug.c b/engines/sci/engine/scriptdebug.c new file mode 100644 index 0000000000..1a4b53e342 --- /dev/null +++ b/engines/sci/engine/scriptdebug.c @@ -0,0 +1,3887 @@ +/*************************************************************************** + scriptdebug.c Copyright (C) 1999 Christoph Reichenbach, TU Darmstadt + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ +/* Script debugger functionality. Absolutely not threadsafe. */ + +#include "gc.h" +#include +#include +#include +#include +#include +#include "kernel_types.h" +#include +#include +#include + +#ifdef _WIN32 +# include +# include +# include +#endif +#ifdef HAVE_UNISTD_H +# include +/* Assume this is a sufficient precondition */ +# include +#endif + +extern int debug_sleeptime_factor; +int _debugstate_valid = 0; /* Set to 1 while script_debug is running */ +int _debug_step_running = 0; /* Set to >0 to allow multiple stepping */ +int _debug_commands_not_hooked = 1; /* Commands not hooked to the console yet? */ +int _debug_seeking = 0; /* Stepping forward until some special condition is met */ +int _debug_seek_level = 0; /* Used for seekers that want to check their exec stack depth */ +int _debug_seek_special = 0; /* Used for special seeks(1) */ +int _weak_validations = 1; /* Some validation errors are reduced to warnings if non-0 */ +reg_t _debug_seek_reg = NULL_REG_INITIALIZER; /* Used for special seeks(2) */ + +#define _DEBUG_SEEK_NOTHING 0 +#define _DEBUG_SEEK_CALLK 1 /* Step forward until callk is found */ +#define _DEBUG_SEEK_LEVEL_RET 2 /* Step forward until returned from this level */ +#define _DEBUG_SEEK_SPECIAL_CALLK 3 /* Step forward until a /special/ callk is found */ +#define _DEBUG_SEEK_SO 5 /* Step forward until specified PC (after the send command) and stack depth */ +#define _DEBUG_SEEK_GLOBAL 6 /* Step forward until one specified global variable is modified */ + +static reg_t *p_pc; +static stack_ptr_t *p_sp; +static stack_ptr_t *p_pp; +static reg_t *p_objp; +static int *p_restadjust; +static seg_id_t *p_var_segs; +static reg_t **p_vars; +static reg_t **p_var_base; +static int *p_var_max; /* May be NULL even in valid state! */ + +static const int MIDI_cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 1, 1, 2, 0}; + +int _kdebug_cheap_event_hack = 0; +int _kdebug_cheap_soundcue_hack = -1; + +char inputbuf[256] = ""; + +#define LOOKUP_SPECIES(species) (\ + (species >= 1000)? species : *(s->classtable[species].scriptposp) \ + + s->classtable[species].class_offset) + +const char * +_debug_get_input_default(void) +{ + char newinpbuf[256]; + + printf("> "); + fgets(newinpbuf, 254, stdin); + + if (strlen(newinpbuf) != 0) + memcpy(inputbuf, newinpbuf, 256); + + return inputbuf; +} + + +static inline int +_parse_ticks(byte *data, int *offset_p, int size) +{ + int ticks = 0; + int tempticks; + int offset = 0; + + do { + tempticks = data[offset++]; + ticks += (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX)? + SCI_MIDI_TIME_EXPANSION_LENGTH : tempticks; + } while (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX + && offset < size); + + if (offset_p) + *offset_p = offset; + + return ticks; +} + + +static void +midi_hexdump(byte *data, int size, int notational_offset) +{ /* Specialised for SCI01 tracks (this affects the way cumulative cues are treated ) */ + int offset = 0; + int prev = 0; + + if (*data == 0xf0) /* SCI1 priority spec */ + offset = 8; + + while (offset < size) { + int old_offset = offset; + int offset_mod; + int time = _parse_ticks(data + offset, &offset_mod, + size); + int cmd; + int pleft; + int firstarg; + int i; + int blanks = 0; + + offset += offset_mod; + fprintf(stderr, " [%04x] %d\t", + old_offset + notational_offset, time); + + cmd = data[offset]; + if (!(cmd & 0x80)) { + cmd = prev; + if (prev < 0x80) { + fprintf(stderr, "Track broken at %x after" + " offset mod of %d\n", + offset + notational_offset, offset_mod); + sci_hexdump(data, size, notational_offset); + return; + } + fprintf(stderr, "(rs %02x) ", cmd); + blanks += 8; + } else { + ++offset; + fprintf(stderr, "%02x ", cmd); + blanks += 3; + } + prev = cmd; + + pleft = MIDI_cmdlen[cmd >> 4]; + if (SCI_MIDI_CONTROLLER(cmd) && data[offset] + == SCI_MIDI_CUMULATIVE_CUE) + --pleft; /* This is SCI(0)1 specific */ + + for (i = 0; i < pleft; i++) { + if (i == 0) + firstarg = data[offset]; + fprintf(stderr, "%02x ", data[offset++]); + blanks += 3; + } + + while (blanks < 16) { + blanks += 4; + fprintf(stderr, " "); + } + + while (blanks < 20) { + ++blanks; + fprintf(stderr, " "); + } + + if (cmd == SCI_MIDI_EOT) + fprintf(stderr, ";; EOT"); + else if (cmd == SCI_MIDI_SET_SIGNAL) { + if (firstarg == SCI_MIDI_SET_SIGNAL_LOOP) + fprintf(stderr, ";; LOOP point"); + else + fprintf(stderr, ";; CUE (%d)", firstarg); + } else if (SCI_MIDI_CONTROLLER(cmd)) { + if (firstarg == SCI_MIDI_CUMULATIVE_CUE) + fprintf(stderr, ";; CUE (cumulative)"); + else if (firstarg == SCI_MIDI_RESET_ON_SUSPEND) + fprintf(stderr, ";; RESET-ON-SUSPEND flag"); + } + fprintf(stderr, "\n"); + + if (old_offset >= offset) { + fprintf(stderr, "-- Not moving forward anymore," + " aborting (%x/%x)\n", offset, old_offset); + return; + } + } +} + +#define SONGDATA(x) data[offset + (x)] +#define CHECK_FOR_END_ABSOLUTE(off) if ((off) >= size) return; +static void +sci01_song_header_dump(byte *data, int size) +{ + int offset = 0; + int smallest_start = 10000; + + sciprintf ("SCI01 song track mappings:\n"); + + if (*data == 0xf0) /* SCI1 priority spec */ + offset = 8; + + CHECK_FOR_END_ABSOLUTE(0); + while (SONGDATA(0) != 0xff) { + byte device_id = data[offset]; + sciprintf("* Device %02x:\n", device_id); + offset++; + CHECK_FOR_END_ABSOLUTE(offset + 1); + while (SONGDATA(0) != 0xff) { + int track_offset; + int end; + byte header1, header2; + + CHECK_FOR_END_ABSOLUTE(offset + 7); + + offset += 2; + + track_offset = getUInt16(data + offset); + header1 = data[track_offset]; + header2 = data[track_offset+1]; + track_offset += 2; + + if (track_offset < smallest_start) + smallest_start = track_offset; + end = getUInt16(data + offset + 2); + sciprintf(" - %04x -- %04x", + track_offset, track_offset + end); + + if (track_offset == 0xfe) + sciprintf(" (PCM data)\n"); + else + sciprintf(" (channel %d, special %d," + " %d playing notes, %d foo)\n", + header1 & 0xf, + header1 >> 4, + header2 & 0xf, + header2 >> 4); + + offset += 4; + } + offset++; + } +} +#undef CHECK_FOR_END_ABSOLUTE +#undef SONGDATA + + + +int c_sfx_01_header(state_t *s) +{ + resource_t *song = scir_find_resource(s->resmgr, + sci_sound, + cmd_params[0].val, + 0); + + if (!song) { + sciprintf("Doesn't exist\n"); + return 1; + } + + sci01_song_header_dump(song->data, song->size); + return 0; +} + +int c_sfx_01_track(state_t *s) +{ + resource_t *song = scir_find_resource(s->resmgr, + sci_sound, + cmd_params[0].val, + 0); + + int offset = cmd_params[1].val; + + if (!song) { + sciprintf("Doesn't exist\n"); + return 1; + } + + midi_hexdump(song->data + offset, song->size, offset); + return 0; +} + + + +const char *(*_debug_get_input)(void) = _debug_get_input_default; + +int +c_segtable(state_t *s) +{ + int i; + + sciprintf(" ---- segment table ----\n"); + for (i = 0; i < s->seg_manager.heap_size; i++) { + mem_obj_t *mobj = s->seg_manager.heap[i]; + if (mobj && mobj->type) { + sciprintf(" [%04x] ", i); + + switch (mobj->type) { + + case MEM_OBJ_SCRIPT: + sciprintf("S script.%03d l:%d ", + mobj->data.script.nr, + mobj->data.script.lockers); + break; + + case MEM_OBJ_CLONES: + sciprintf("C clones (%d allocd)", + mobj->data.clones.entries_used); + break; + + case MEM_OBJ_LOCALS: + sciprintf("V locals %03d", + mobj->data.locals.script_id); + break; + + case MEM_OBJ_STACK: + sciprintf("D data stack (%d)", + mobj->data.stack.nr); + break; + + case MEM_OBJ_SYS_STRINGS: + sciprintf("Y system string table"); + break; + + case MEM_OBJ_LISTS: + sciprintf("L lists (%d)", mobj->data.lists.entries_used); + break; + + case MEM_OBJ_NODES: + sciprintf("N nodes (%d)", mobj->data.nodes.entries_used); + break; + + case MEM_OBJ_HUNK: + sciprintf("H hunk (%d)", mobj->data.hunks.entries_used); + break; + + case MEM_OBJ_DYNMEM: + sciprintf("M dynmem: %d bytes", mobj->data.dynmem.size); + break; + + default: + sciprintf("I Invalid (type = %x)", mobj->type); + break; + } + + sciprintf(" seg_ID = %d \n", mobj->segmgr_id); + } + } + sciprintf("\n"); + return 0; +} + + +static void +print_obj_head(state_t *s, object_t *obj) +{ + sciprintf(PREG" %s : %3d vars, %3d methods\n", + PRINT_REG(obj->pos), + obj_get_name(s, obj->pos), + obj->variables_nr, + obj->methods_nr); +} + +static void +print_list(state_t *s, list_t *l) +{ + reg_t pos = l->first; + reg_t my_prev = NULL_REG_INITIALIZER; + + sciprintf("\t<\n"); + + while (!IS_NULL_REG(pos)) { + node_t *node; + mem_obj_t *mobj = GET_SEGMENT(s->seg_manager, pos.segment, MEM_OBJ_NODES); + + if (!mobj || !ENTRY_IS_VALID(&(mobj->data.nodes), pos.offset)) { + sciprintf(" WARNING: "PREG": Doesn't contain list node!\n", + PRINT_REG(pos)); + return; + } + + node = &(mobj->data.nodes.table[pos.offset].entry); + + sciprintf("\t"PREG" : "PREG" -> "PREG"\n", + PRINT_REG(pos), PRINT_REG(node->key), PRINT_REG(node->value)); + + if (!REG_EQ(my_prev, node->pred)) + sciprintf(" WARNING: current node gives "PREG" as predecessor!\n", + PRINT_REG(node->pred)); + + my_prev = pos; + pos = node->succ; + } + + if (!REG_EQ(my_prev, l->last)) + sciprintf(" WARNING: Last node was expected to be "PREG", was "PREG"!\n", + PRINT_REG(l->last), PRINT_REG(my_prev)); + sciprintf("\t>\n"); +} + + +static void +_c_single_seg_info(state_t *s, mem_obj_t *mobj) +{ + switch (mobj->type) { + + case MEM_OBJ_SCRIPT: { + int i; + script_t *scr = &(mobj->data.script); + sciprintf("script.%03d locked by %d, bufsize=%d (%x)\n", + scr->nr, scr->lockers, scr->buf_size, scr->buf_size); + if (scr->export_table) + sciprintf(" Exports: %4d at %d\n", + scr->exports_nr, + ((byte *) scr->export_table) - ((byte *)scr->buf)); + else + sciprintf(" Exports: none\n"); + + sciprintf(" Synynms: %4d\n", scr->synonyms_nr); + + if (scr->locals_block) + sciprintf(" Locals : %4d in segment 0x%x\n", + scr->locals_block->nr, + scr->locals_segment); + else + sciprintf(" Locals : none\n"); + + sciprintf(" Objects: %4d\n", + scr->objects_nr); + for (i = 0; i < scr->objects_nr; i++) { + sciprintf(" "); + print_obj_head(s, scr->objects + i); + } + } + break; + + case MEM_OBJ_LOCALS: { + local_variables_t *locals = &(mobj->data.locals); + sciprintf("locals for script.%03d\n", locals->script_id); + sciprintf(" %d (0x%x) locals\n", locals->nr, locals->nr); + } + break; + + case MEM_OBJ_STACK: { + dstack_t *stack = &(mobj->data.stack); + sciprintf("stack\n"); + sciprintf(" %d (0x%x) entries\n", stack->nr, stack->nr); + } + break; + + case MEM_OBJ_SYS_STRINGS: { + sys_strings_t *strings = &(mobj->data.sys_strings); + int i; + + sciprintf("system string table\n"); + for (i = 0; i < SYS_STRINGS_MAX; i++) + if (strings->strings[i].name) + sciprintf(" %s[%d]=\"%s\"\n", + strings->strings[i].name, + strings->strings[i].max_size, + strings->strings[i].value); + } + break; + + case MEM_OBJ_CLONES: { + int i = 0; + clone_table_t *ct = + &(mobj->data.clones); + + sciprintf("clones\n"); + + for (i = 0; i < ct->max_entry; i++) + if (ENTRY_IS_VALID(ct, i)) { + sciprintf(" [%04x] ", i); + print_obj_head(s, &(ct->table[i].entry)); + } + } + break; + + case MEM_OBJ_LISTS: { + int i = 0; + list_table_t *lt = + &(mobj->data.lists); + + sciprintf("lists\n"); + for (i = 0; i < lt->max_entry; i++) + if (ENTRY_IS_VALID(lt, i)) { + sciprintf(" [%04x]: ", i); + print_list(s, &(lt->table[i].entry)); + } + } + break; + + case MEM_OBJ_NODES: { + sciprintf("nodes (total %d)\n", mobj->data.nodes.entries_used); + break; + } + + case MEM_OBJ_HUNK: { + int i; + hunk_table_t *ht = + &(mobj->data.hunks); + + sciprintf("hunk (total %d)\n", mobj->data.hunks.entries_used); + for (i = 0; i < ht->max_entry; i++) + if (ENTRY_IS_VALID(ht, i)) { + sciprintf(" [%04x] %d bytes at %p, type=%s\n", + i, ht->table[i].entry.size, ht->table[i].entry.mem, + ht->table[i].entry.type); + } + } + + case MEM_OBJ_DYNMEM: { + sciprintf("dynmem (%s): %d bytes\n", + mobj->data.dynmem.description? + mobj->data.dynmem.description:"no description", + mobj->data.dynmem.size); + + sci_hexdump(mobj->data.dynmem.buf, mobj->data.dynmem.size, 0); + } + break; + + default : sciprintf("Invalid type %d\n", mobj->type); + break; + } +} + +static int +show_node(state_t *s, reg_t addr) +{ + + mem_obj_t *mobj = GET_SEGMENT(s->seg_manager, addr.segment, MEM_OBJ_LISTS); + + if (mobj) { + list_table_t *lt = &(mobj->data.lists); + list_t *list; + + if (!ENTRY_IS_VALID(lt, addr.offset)) { + sciprintf("Address does not contain a list\n"); + return 1; + } + + list = &(lt->table[addr.offset].entry); + + sciprintf(PREG" : first x last = ("PREG", "PREG")\n", + PRINT_REG(addr), PRINT_REG(list->first), + PRINT_REG(list->last)); + } else { + node_table_t *nt; + node_t *node; + mobj = GET_SEGMENT(s->seg_manager, addr.segment, MEM_OBJ_NODES); + + if (!mobj) { + sciprintf("Segment #%04x is not a list or node segment\n", addr.segment); + return 1; + } + + nt = &(mobj->data.nodes); + + if (!ENTRY_IS_VALID(nt, addr.offset)) { + sciprintf("Address does not contain a node\n"); + return 1; + } + node = &(nt->table[addr.offset].entry); + + sciprintf(PREG" : prev x next = ("PREG", "PREG"); maps "PREG" -> "PREG"\n", + PRINT_REG(addr), + PRINT_REG(node->pred), + PRINT_REG(node->succ), + PRINT_REG(node->key), + PRINT_REG(node->value)); + } + return 0; +} + +int objinfo(state_t *s, reg_t pos); + +void +song_lib_dump(songlib_t songlib, int line); + +static int +c_songlib_print(state_t *s) +{ + song_lib_dump(s->sound.songlib, __LINE__); + return 0; +} + +static int +c_vr(state_t *s) +{ + reg_t reg = cmd_params[0].reg; + reg_t reg_end = cmd_paramlength > 1 ? cmd_params[1].reg : NULL_REG; + int type_mask = determine_reg_type(s, reg, 1); + int filter; + int found = 0; + + sciprintf(PREG" is of type 0x%x%s: ", PRINT_REG(reg), type_mask & ~KSIG_INVALID, + type_mask & KSIG_INVALID ? " (invalid)" : ""); + + type_mask &= ~KSIG_INVALID; + + if (reg.segment == 0 + && reg.offset == 0) { + sciprintf("Null.\n"); + return 0; + } + + if (reg_end.segment != reg.segment) + { + sciprintf("Ending segment different from starting segment. Assuming no bound on dump.\n"); + reg_end = NULL_REG; + } + + for (filter = 1; filter < 0xf000; filter <<= 1) { + int type = type_mask & filter; + + if (found && type) { + sciprintf("--- Alternatively, it could be a "); + } + + + switch (type) { + case 0: break; + + case KSIG_LIST: { + list_t *l = LOOKUP_LIST(reg); + + sciprintf("list\n"); + + if (l) + print_list(s, l); + else + sciprintf("Invalid list.\n"); + } + break; + + case KSIG_NODE: + sciprintf("list node\n"); + show_node(s, reg); + break; + + case KSIG_OBJECT: + sciprintf("object\n"); + objinfo(s, reg); + break; + + case KSIG_REF: { + int size; + unsigned char *block = sm_dereference(&s->seg_manager, + reg, &size); + + sciprintf("raw data\n"); + + if (reg_end.segment != 0 && size < reg_end.offset - reg.offset) + { + sciprintf("Block end out of bounds (size %d). Resetting.\n", size); + reg_end = NULL_REG; + } + + if (reg_end.segment != 0 && (size >= reg_end.offset - reg.offset)) + size = reg_end.offset - reg.offset; + + if (reg_end.segment != 0) + sciprintf("Block size less than or equal to %d\n", size); + + sci_hexdump(block, size, 0); + } + break; + + case KSIG_ARITHMETIC: + sciprintf("arithmetic value\n %d (%04x)\n", (gint16) reg.offset, reg.offset); + break; + + + default: sciprintf("unknown.\n", type); + + } + + if (type) { + sciprintf("\n"); + found = 1; + } + } + + return 0; +} + + +int +c_segkill(state_t *s) +{ + int i = 0; + while (i < cmd_paramlength) { + int nr = cmd_params[i++].val; + + sm_set_lockers(&(s->seg_manager), nr, 0, SEG_ID); + } + return 0; +} + +static int +c_mousepos(state_t *s) +{ + sci_event_t event; + + sciprintf("Click somewhere in the game window...\n"); + + while (event = gfxop_get_event(s->gfx_state, SCI_EVT_MOUSE_RELEASE), + event.type != SCI_EVT_MOUSE_RELEASE) + { + }; + + sciprintf("Mouse pointer at (%d, %d)\n", + s->gfx_state->pointer_pos.x, + s->gfx_state->pointer_pos.y); + return 0; +} + +int +c_seginfo(state_t *s) +{ + int i = 0; + + if (cmd_paramlength) { + while (i < cmd_paramlength) { + int nr = cmd_params[i++].val; + if (nr < 0 + || nr >= s->seg_manager.heap_size + || !s->seg_manager.heap[nr]) { + sciprintf("Segment %04x does not exist\n", nr); + return 1; + } + sciprintf("[%04x] ", nr); + _c_single_seg_info(s, s->seg_manager.heap[nr]); + } + } else for (i = 0; i < s->seg_manager.heap_size; i++) + if (s->seg_manager.heap[i]) { + sciprintf("[%04x] ", i); + _c_single_seg_info(s, s->seg_manager.heap[i]); + sciprintf("\n"); + } + return 0; +} + +int +c_debuginfo(state_t *s) +{ + exec_stack_t *eframe = NULL; + + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + if (s->execution_stack && s->execution_stack_pos >= 0) + eframe = s->execution_stack + s->execution_stack_pos; + + sciprintf("acc="PREG" prev="PREG" &rest=%x\n", + PRINT_REG(s->r_acc), + PRINT_REG(s->r_prev), *p_restadjust); + if (eframe) + sciprintf("pc="PREG" obj="PREG" fp="PSTK" sp="PSTK"\n", + PRINT_REG(*p_pc), + PRINT_REG(*p_objp), + PRINT_STK(*p_pp), + PRINT_STK(*p_sp)); + else + sciprintf("\n"); + return 0; +} + + +int +c_step(state_t *s) +{ + _debugstate_valid = 0; + if (cmd_paramlength && (cmd_params[0].val > 0)) + _debug_step_running = cmd_params[0].val - 1; + return 0; +} + +#ifdef __GNUC__ +#warning "Re-implement con:so" +#endif +#if 0 +int +c_stepover(state_t *s) +{ + int opcode, opnumber; + + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + _debugstate_valid = 0; + opcode = s->heap [*p_pc]; + opnumber = opcode >> 1; + if (opnumber == 0x22 /* callb */ || opnumber == 0x23 /* calle */ || + opnumber == 0x25 /* send */ || opnumber == 0x2a /* self */ || + opnumber == 0x2b /* super */) + { + _debug_seeking = _DEBUG_SEEK_SO; + _debug_seek_level = s->execution_stack_pos; + /* Store in _debug_seek_special the offset of the next command after send */ + switch (opcode) + { + case 0x46: /* calle W */ + _debug_seek_special = *p_pc + 5; break; + + case 0x44: /* callb W */ + case 0x47: /* calle B */ + case 0x56: /* super W */ + _debug_seek_special = *p_pc + 4; break; + + case 0x45: /* callb B */ + case 0x57: /* super B */ + case 0x4A: /* send W */ + case 0x54: /* self W */ + _debug_seek_special = *p_pc + 3; break; + + default: + _debug_seek_special = *p_pc + 2; + } + } + + return 0; +} +#endif + +int +c_sim_parse(state_t *s) +{ + unsigned int i; + const char *operators = ",&/()[]#<>"; + + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + if (cmd_paramlength == 0) { + s->parser_valid = 0; + return 0; + } + + for (i = 0; i < cmd_paramlength; i++) { + int flag = 0; + char *token = cmd_params[i].str; + + if (strlen(token) == 1) {/* could be an operator */ + int j = 0; + while (operators[j] && (operators[j] != token[0])) + j++; + if (operators[j]) { + s->parser_nodes[i].type = 1; + s->parser_nodes[i].content.value = j + 0xf0; + flag = 1; /* found an operator */ + } + } + + if (!flag) { + char *openb = strchr(token, '['); /* look for opening braces */ + result_word_t *result; + + if (openb) + *openb = 0; /* remove them and the rest */ + + result = vocab_lookup_word(token, strlen(token), + s->parser_words, s->parser_words_nr, + s->parser_suffices, s->parser_suffices_nr); + + if (result) { + s->parser_nodes[i].type = 0; + s->parser_nodes[i].content.value = result->group; + free(result); + } else { /* group name was specified directly? */ + int val = strtol(token, NULL, 0); + if (val) { + s->parser_nodes[i].type = 0; + s->parser_nodes[i].content.value = val; + } else { /* invalid and not matched */ + sciprintf("Couldn't interpret '%s'\n", token); + s->parser_valid = 0; + return 1; + } + } + } + + } + + s->parser_nodes[cmd_paramlength].type = -1; /* terminate */ + + s->parser_valid = 2; + return 0; +} + + +int +c_classtable(state_t *s) +{ + int i; + + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + sciprintf("Available classes:\n"); + for (i = 0; i < s->classtable_size; i++) + if (s->classtable[i].reg.segment) + sciprintf(" Class 0x%x at "PREG" (script 0x%x)\n", + i, + PRINT_REG(s->classtable[i].reg), + s->classtable[i].script); + + return 0; +} + +int +c_viewinfo(state_t *s) +{ + int view = cmd_params[0].val; + int palette = cmd_params[1].val; + int loops, i; + gfxr_view_t *view_pixmaps = NULL; + + if (!s) { + sciprintf("Not in debug state\n"); + return 1; + } + sciprintf("Resource view.%d ", view); + + loops = gfxop_lookup_view_get_loops(s->gfx_state, view); + + if (loops < 0) + sciprintf("does not exist.\n"); + else { + sciprintf("has %d loops:\n", loops); + + for (i = 0; i < loops; i++) { + int j, cels; + + sciprintf("Loop %d: %d cels.\n", i, cels = gfxop_lookup_view_get_cels(s->gfx_state, view, i)); + for (j = 0; j < cels; j++) { + int width; + int height; + point_t mod; + + if (con_can_handle_pixmaps()) { + view_pixmaps = gfxr_get_view(s->gfx_state->resstate, + view, &i, &j, palette); + con_insert_pixmap(gfx_clone_pixmap(view_pixmaps->loops[i].cels[j], + s->gfx_state->driver->mode)); + } + + gfxop_get_cel_parameters(s->gfx_state, view, i, j, &width, &height, &mod); + + sciprintf(" cel %d: size %dx%d, adj+(%d,%d)\n",j, width, height, mod.x, mod.y); + } + } + } + return 0; +} + +int +c_list_sentence_fragments(state_t *s) +{ + int i; + + if (!s) { + sciprintf("Not in debug state\n"); + return 1; + } + + for (i = 0; i < s->parser_branches_nr; i++) { + int j = 0; + + sciprintf("R%02d: [%x] ->", i, s->parser_branches[i].id); + while ((j < 10) && s->parser_branches[i].data[j]) { + int dat = s->parser_branches[i].data[j++]; + + switch (dat) { + case VOCAB_TREE_NODE_COMPARE_TYPE: + dat = s->parser_branches[i].data[j++]; + sciprintf(" C(%x)", dat); + break; + + case VOCAB_TREE_NODE_COMPARE_GROUP: + dat = s->parser_branches[i].data[j++]; + sciprintf(" WG(%x)", dat); + break; + + case VOCAB_TREE_NODE_FORCE_STORAGE: + dat = s->parser_branches[i].data[j++]; + sciprintf(" FORCE(%x)", dat); + break; + + default: + if (dat > VOCAB_TREE_NODE_LAST_WORD_STORAGE) { + int dat2 = s->parser_branches[i].data[j++]; + sciprintf(" %x[%x]", dat, dat2); + } else + sciprintf(" ?%x?", dat); + } + } + sciprintf("\n"); + } + + sciprintf("%d rules.\n", s->parser_branches_nr); + return 0; +} + +enum { + _parse_eoi, + _parse_token_pareno, + _parse_token_parenc, + _parse_token_nil, + _parse_token_number +}; + +int +_parse_getinp(int *i, int *nr) +{ + char *token; + + if ((unsigned)*i == cmd_paramlength) + return _parse_eoi; + + token = cmd_params[(*i)++].str; + + if (!strcmp(token,"(")) + return _parse_token_pareno; + + if (!strcmp(token,")")) + return _parse_token_parenc; + + if (!strcmp(token,"nil")) + return _parse_token_nil; + + *nr = strtol(token, NULL, 0); + return _parse_token_number; +} + +int +_parse_nodes(state_t *s, int *i, int *pos, int type, int nr) +{ + int nexttk, nextval, newpos, oldpos; + + if (type == _parse_token_nil) + return 0; + + if (type == _parse_token_number) { + s->parser_nodes[*pos += 1].type = PARSE_TREE_NODE_LEAF; + s->parser_nodes[*pos].content.value = nr; + return *pos; + } + if (type == _parse_eoi) { + sciprintf("Unbalanced parentheses\n"); + return -1; + } + if (type == _parse_token_parenc) { + sciprintf("Syntax error at token %d\n", *i); + return -1; + } + s->parser_nodes[oldpos = ++(*pos)].type = PARSE_TREE_NODE_BRANCH; + + nexttk = _parse_getinp(i, &nextval); + if ((newpos = s->parser_nodes[oldpos].content.branches[0] = _parse_nodes(s, i, pos, nexttk, nextval)) == -1) + return -1; + + nexttk = _parse_getinp(i, &nextval); + if ((newpos = s->parser_nodes[oldpos].content.branches[1] = _parse_nodes(s, i, pos, nexttk, nextval)) == -1) + return -1; + + if (_parse_getinp(i, &nextval) != _parse_token_parenc) + sciprintf("Expected ')' at token %d\n", *i); + return oldpos; +} + +int +c_set_parse_nodes(state_t *s) +{ + int i = 0; + int foo, bar; + int pos = -1; + + if (!s) { + sciprintf("Not in debug state\n"); + return 1; + } + + bar = _parse_getinp(&i, &foo); + if (_parse_nodes(s, &i, &pos, bar, foo) == -1) + return 1; + + vocab_dump_parse_tree("debug-parse-tree", s->parser_nodes); + return 0; +} + +/* from grammar.c: */ +int +vocab_gnf_parse(parse_tree_node_t *nodes, result_word_t *words, int words_nr, + parse_tree_branch_t *branch0, parse_rule_list_t *tlist, int verbose); +/* parses with a GNF rule set */ + +int +c_parse(state_t *s) +{ + result_word_t *words; + int words_nr; + char *error; + char *string; + + if (!s) { + sciprintf("Not in debug state\n"); + return 1; + } + + string = cmd_params[0].str; + sciprintf("Parsing '%s'\n", string); + words = vocab_tokenize_string(string, &words_nr, + s->parser_words, s->parser_words_nr, + s->parser_suffices, s->parser_suffices_nr, + &error); + if (words) { + + int i, syntax_fail = 0; + + vocab_synonymize_tokens(words, words_nr, s->synonyms, s->synonyms_nr); + + sciprintf("Parsed to the following blocks:\n"); + + for (i = 0; i < words_nr; i++) + sciprintf(" Type[%04x] Group[%04x]\n", words[i].w_class, words[i].group); + + if (vocab_gnf_parse(&(s->parser_nodes[0]), words, words_nr, s->parser_branches, + s->parser_rules, 1)) + syntax_fail = 1; /* Building a tree failed */ + + free(words); + + if (syntax_fail) + sciprintf("Building a tree failed.\n"); + else + vocab_dump_parse_tree("debug-parse-tree", s->parser_nodes); + + } else { + + sciprintf("Unknown word: '%s'\n", error); + free(error); + } + return 0; +} + +int +c_save_game(state_t *s) +{ + int omit_check = cmd_params[0].str[0] == '_'; + int i; + + if (!s) { + sciprintf("Not in debug state\n"); + return 1; + } + + if (!omit_check) { + int result = 0; + for (i = 0; i < s->file_handles_nr; i++) + if (s->file_handles[i]) + result++; + + if (result) { + sciprintf("Game state has %d open file handles.\n", result); + sciprintf("Save to '_%s' to ignore this check.\nGame was NOT saved.\n", cmd_params[0].str); + return 1; + } + } + + if (gamestate_save(s, cmd_params[0].str)) { + sciprintf("Saving the game state to '%s' failed\n", cmd_params[0].str); + } + + return 0; +} + +int +c_restore_game(state_t *s) +{ + state_t *newstate; + + if (!s) { + sciprintf("Not in debug state\n"); + return 1; + } + + newstate = gamestate_restore(s, cmd_params[0].str); + + if (newstate) { + + s->successor = newstate; /* Set successor */ + + script_abort_flag = SCRIPT_ABORT_WITH_REPLAY; /* Abort current game */ + _debugstate_valid = 0; + s->execution_stack_pos = s->execution_stack_base; + + return 0; + } else { + sciprintf("Restoring gamestate '%s' failed.\n", cmd_params[0].str); + return 1; + } +} + +extern char *old_save_dir; /* Ouch */ + +int +c_restart_game(state_t *s) +{ + unsigned int i; + char *deref_save_dir = (char*)kernel_dereference_bulk_pointer(s, s->save_dir_copy, 1); + + if (!s) { + sciprintf("Not in debug state\n"); + return 1; + } + + old_save_dir = strdup(deref_save_dir); + for (i = 0; i < cmd_paramlength; i++) { + if ((strcmp(cmd_params[0].str, "-r") == 0) + || (strcmp(cmd_params[0].str, "--replay") == 0)) + s->restarting_flags |= SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE; + else + if ((strcmp(cmd_params[0].str, "-p") == 0) + || (strcmp(cmd_params[0].str, "--play") == 0)) + s->restarting_flags &= ~SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE; + else { + sciprintf("Invalid parameter '%s'\n", cmd_params[0].str); + return 1; + } + } + + sciprintf("Restarting\n"); + + s->restarting_flags |= SCI_GAME_IS_RESTARTING_NOW; + + script_abort_flag = 1; + _debugstate_valid = 0; + return 0; +} + + +int +c_stack(state_t *s) +{ + int i; + exec_stack_t *xs; + + if (!s) { + sciprintf("Not in debug state\n"); + return 1; + } + + if (s->execution_stack_pos >= 0) + xs = s->execution_stack + s->execution_stack_pos; + else { + sciprintf("No exec stack!"); + return 1; + } + + for (i = cmd_params[0].val ; i > 0; i--) { + if ((xs->sp - xs->fp - i) == 0) + sciprintf("-- temp variables --\n"); + if (xs->sp - i >= s->stack_base) + sciprintf(PSTK" = "PREG"\n", + PRINT_STK(xs->sp - i), PRINT_REG(xs->sp[-i])); + } + + return 0; +} + +const char *selector_name(state_t *s, int selector) +{ + if (selector >= 0 && selector < s->selector_names_nr) + return s->selector_names[selector]; + else + return "--INVALID--"; +} + +int prop_ofs_to_id(state_t *s, int prop_ofs, reg_t objp) +{ + object_t *obj = obj_get(s, objp); + byte *selectoroffset; + int selectors; + + if (!obj) { + sciprintf("Applied prop_ofs_to_id on non-object at "PREG"\n", + PRINT_REG(objp)); + return -1; + } + + selectors = obj->variables_nr; + + if (s->version < SCI_VERSION(1,001,000)) + selectoroffset = ((byte *) (obj->base_obj)) + SCRIPT_SELECTOR_OFFSET + selectors * 2; + else + { + if (!(obj->variables[SCRIPT_INFO_SELECTOR].offset & SCRIPT_INFO_CLASS)) + { + obj = obj_get(s, obj->variables[SCRIPT_SUPERCLASS_SELECTOR]); + selectoroffset = (byte *) obj->base_vars; + } else selectoroffset = (byte *) obj->base_vars; + } + + if (prop_ofs < 0 || (prop_ofs >> 1) >= selectors) { + sciprintf("Applied prop_ofs_to_id to invalid property offset %x (property #%d not" + " in [0..%d]) on object at "PREG"\n", + prop_ofs, prop_ofs >> 1, selectors - 1, + PRINT_REG(objp)); + return -1; + } + + + return getUInt16(selectoroffset + prop_ofs); +} + +reg_t +disassemble(state_t *s, reg_t pos, int print_bw_tag, int print_bytecode) +/* Disassembles one command from the heap, returns address of next command or 0 if a ret was +** encountered. +*/ +{ + mem_obj_t *memobj = GET_SEGMENT(s->seg_manager, pos.segment, MEM_OBJ_SCRIPT); + script_t *script_entity = NULL; + byte *scr; + int scr_size; + reg_t retval = make_reg(pos.segment, pos.offset + 1); + word param_value; + int opsize; + int opcode; + int bytecount = 1; + int i = 0; + + if (!memobj) { + sciprintf("Disassembly failed: Segment %04x non-existant or not a script\n", + pos.segment); + return retval; + } else script_entity = &(memobj->data.script); + + scr = script_entity->buf; + scr_size = script_entity->buf_size; + + if (pos.offset >= scr_size) { + sciprintf("Trying to disassemble beyond end of script\n"); + return pos; + } + + opsize = scr[pos.offset]; + opcode = opsize >> 1; + + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return retval; + } + + opsize &= 1; /* byte if true, word if false */ + + + sciprintf(PREG": ", PRINT_REG(pos)); + + if (print_bytecode) { + while (formats[opcode][i]) { + switch (formats[opcode][i++]) { + + case Script_SByte: + case Script_Byte: bytecount++; + break; + + case Script_Word: + case Script_SWord: bytecount += 2; + break; + + case Script_SVariable: + case Script_Variable: + case Script_Property: + case Script_Global: + case Script_Local: + case Script_Temp: + case Script_Param: + case Script_SRelative: + if (opsize) + bytecount ++; + else + bytecount += 2; + break; + + default: + break; + } + } + + if (pos.offset + bytecount > scr_size) { + sciprintf("Operation arguments extend beyond end of script\n"); + return retval; + } + + for (i = 0; i < bytecount; i++) + sciprintf("%02x ", scr[pos.offset + i]); + + for (i = bytecount; i < 5; i++) + sciprintf(" "); + + } + + if (print_bw_tag) + sciprintf("[%c] ", opsize? 'B' : 'W'); + sciprintf("%s", s->opcodes[opcode].name); + + i = 0; + while (formats[opcode][i]) + + switch (formats[opcode][i++]) { + + case Script_Invalid: sciprintf("-Invalid operation-"); break; + + case Script_SByte: + case Script_Byte: sciprintf(" %02x", scr[retval.offset++]); break; + + case Script_Word: + case Script_SWord: + sciprintf(" %04x", 0xffff & (scr[retval.offset] + | (scr[retval.offset+1] << 8))); + retval.offset += 2; + break; + + case Script_SVariable: + case Script_Variable: + case Script_Property: + case Script_Global: + case Script_Local: + case Script_Temp: + case Script_Param: + if (opsize) + param_value = scr[retval.offset++]; + else { + param_value = 0xffff & (scr[retval.offset] + | (scr[retval.offset+1] << 8)); + retval.offset += 2; + } + + if (opcode == op_callk) + sciprintf(" %s[%x]", (param_value < s->kfunct_nr) ? + ((param_value < s->kernel_names_nr) ? s->kernel_names[param_value] : "[Unknown(postulated)]") + : "", param_value); + else sciprintf(opsize? " %02x" : " %04x", param_value); + + break; + + case Script_Offset: + if (opsize) + param_value = scr[retval.offset++]; + else { + param_value = 0xffff & (scr[retval.offset] + | (scr[retval.offset+1] << 8)); + retval.offset += 2; + } + sciprintf (opsize? " %02x [%04x]" : " %04x", param_value); + break; + + case Script_SRelative: + if (opsize) + param_value = scr[retval.offset++]; + else { + param_value = 0xffff & (scr[retval.offset] + | (scr[retval.offset+1] << 8)); + retval.offset += 2; + } + sciprintf (opsize? " %02x [%04x]" : " %04x [%04x]", param_value, (0xffff) & (retval.offset + param_value)); + break; + + case Script_End: retval = NULL_REG; + break; + + default: + sciprintf("Internal assertion failed in 'disassemble', %s, L%d\n", __FILE__, __LINE__); + + } + + if (REG_EQ(pos, *p_pc)) /* Extra information if debugging the current opcode */ + + if ((opcode == op_pTos)||(opcode == op_sTop)|| + (opcode == op_pToa)||(opcode == op_aTop)|| + (opcode == op_dpToa)||(opcode == op_ipToa)|| + (opcode == op_dpTos)||(opcode == op_ipTos)) { + int prop_ofs = scr[pos.offset + 1]; + int prop_id = prop_ofs_to_id(s, prop_ofs, *p_objp); + + sciprintf(" (%s)", selector_name(s, prop_id)); + } + + sciprintf("\n"); + + if (REG_EQ(pos, *p_pc)) { /* Extra information if debugging the current opcode */ + + if (opcode == op_callk) { + int stackframe = (scr[pos.offset + 2] >> 1) + + (*p_restadjust); + int argc = ((*p_sp)[- stackframe - 1]).offset; + int i; + + if (s->version >= SCI_VERSION_FTU_NEW_SCRIPT_HEADER) + argc += (*p_restadjust); + + + + sciprintf(" Kernel params: ("); + + for (i = 0; i < argc; i++) { + sciprintf(PREG, PRINT_REG((*p_sp)[i - stackframe])); + if (i+1 < argc) + sciprintf(", "); + } + sciprintf(")\n"); + + } + else if ((opcode == op_send) || (opcode == op_self)) { + int restmod = *p_restadjust; + int stackframe = (scr[pos.offset + 1] >> 1) + restmod; + reg_t *sb = *p_sp; + word selector; + reg_t *val_ref; + reg_t fun_ref; + + while (stackframe > 0) { + int argc = sb[- stackframe + 1].offset; + const char *name = NULL; + reg_t called_obj_addr; + + if (opcode == op_send) + called_obj_addr = s->r_acc; + else if (opcode == op_self) + called_obj_addr = *p_objp; + + selector = sb[- stackframe].offset; + + name = obj_get_name(s, called_obj_addr); + + if (!name) + name = ""; + + sciprintf(" %s::%s[", name, (selector > s->selector_names_nr) + ? "" : selector_name(s,selector)); + + switch (lookup_selector(s, called_obj_addr, selector, + &val_ref, &fun_ref)) { + case SELECTOR_METHOD: + sciprintf("FUNCT"); + argc += restmod; + restmod = 0; + break; + case SELECTOR_VARIABLE: + sciprintf("VAR"); + break; + case SELECTOR_NONE: + sciprintf("INVALID"); + break; + } + + sciprintf("]("); + + while (argc--) { + + sciprintf(PREG, PRINT_REG(sb[- stackframe + 2])); + if (argc) + sciprintf(", "); + stackframe--; + } + + sciprintf(")\n"); + + stackframe -= 2; + } /* while (stackframe > 0) */ + + } /* Send-like opcodes */ + + } /* (heappos == *p_pc) */ + + + return retval; +} + +int +c_dumpnodes(state_t *s) +{ + int end = MIN(cmd_params[0].val, VOCAB_TREE_NODES); + int i; + + + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + for (i = 0; i < end; i++) { + sciprintf(" Node %03x: ", i); + if (s->parser_nodes[i].type == PARSE_TREE_NODE_LEAF) + sciprintf("Leaf: %04x\n", s->parser_nodes[i].content.value); + else + sciprintf("Branch: ->%04x, ->%04x\n", s->parser_nodes[i].content.branches[0], + s->parser_nodes[i].content.branches[1]); + } + + return 0; +} + +static const char *varnames[] = {"global", "local", "temp", "param"}; +static const char *varabbrev = "gltp"; + +int +c_vmvarlist(state_t *s) +{ + int i; + + for (i=0;i<4;i++) { + sciprintf("%s vars at "PREG" ", + varnames[i], + PRINT_REG(make_reg(p_var_segs[i], p_vars[i] - p_var_base[i]))); + if (p_var_max) + sciprintf(" total %d", p_var_max[i]); + sciprintf("\n"); + } + return 0; +} + +int +c_vmvars(state_t *s) +{ + const char *vartype_pre = strchr(varabbrev, *cmd_params[0].str); + int vartype; + int idx = cmd_params[1].val; + + if (!vartype_pre) { + sciprintf("Invalid variable type '%c'\n", + *cmd_params[0].str); + return 1; + } + vartype = vartype_pre - varabbrev; + + if (idx < 0) { + sciprintf("Invalid: negative index\n"); + return 1; + } + if ((p_var_max) + && (p_var_max[vartype] <= idx)) { + sciprintf("Max. index is %d (0x%x)\n", + p_var_max[vartype], p_var_max[vartype]); + return 1; + } + + switch(cmd_paramlength) { + case 2: + sciprintf("%s var %d == "PREG"\n", varnames[vartype], idx, + PRINT_REG(p_vars[vartype][idx])); + break; + + case 3: + p_vars[vartype][idx] = cmd_params[2].reg; + break; + + default: + sciprintf("Too many arguments\n"); + } + return 0; +} + +#ifdef HAVE_SYSV_IPC +static int _codebug_pid = 0; +static int _codebug_stdin[2]; +static int _codebug_stdout[2]; +static int _codebug_commands[2]; /* sends commands to intermediate process */ + +/* Codebugging uses two child processes: +** The first one only performs output "coloring", its child process +** is an actual secondary freesci process +*/ + +#define CODEBUG_BUFSIZE 512 +static const char *_codebug_colstr = "\033[31m\033[1m"; +static const char *_codebug_uncolstr = "\033[0m"; + +static void +codebug_send_command(const char *cmd) +{ + if (_codebug_pid) + write(_codebug_commands[1], cmd, strlen(cmd)); +} + +static void +_print_colored(int from_fd) +{ + char buf[CODEBUG_BUFSIZE + 64]; + char *buf_offset; + int br; + int length_increment; + + strcpy(buf, _codebug_colstr); + length_increment = strlen(_codebug_colstr); + buf_offset = buf + length_increment; + length_increment += strlen(_codebug_uncolstr); + + do { + br = read(from_fd, buf_offset, CODEBUG_BUFSIZE); + if (br > 0) { + strcpy(buf_offset + br, _codebug_uncolstr); + /* Atomic write with colors to nullify risk of + ** interference from fg process */ + write(1, buf, br + length_increment); + } + } while (br == CODEBUG_BUFSIZE); +} + +void +_codebug_kill_children() +{ + if (_codebug_pid) + kill(_codebug_pid, SIGTERM); +} + +void +_codebug_sighandler(int i) +{ + if (i == SIGPIPE || i == SIGTERM) + kill(_codebug_pid, SIGTERM); + + write(1, _codebug_uncolstr, strlen(_codebug_uncolstr)); + fprintf(stderr, "Child process failed, aborting!\n"); + fprintf(stderr, "(if you're using the UNIX sound server, you'll probably get spurious" + " warnings regarding the sound server now...)\n"); + exit(1); +} + +static void +do_codebug(char *path, char *datapath) +{ + pipe(_codebug_stdin); + pipe(_codebug_stdout); + close(_codebug_commands[1]); + + _codebug_pid = fork(); + if (_codebug_pid) { + fd_set fs; + int max_n; + + /* parent process */ + close(_codebug_stdin[0]); + close(_codebug_stdout[1]); + + max_n = _codebug_commands[0]; + if (max_n < _codebug_stdout[0]) + max_n = _codebug_stdout[0]; + + signal(SIGCHLD, _codebug_sighandler); + signal(SIGPIPE, _codebug_sighandler); + signal(SIGTERM, _codebug_sighandler); + + while (1) { /* Until sigchild */ + FD_ZERO(&fs); + FD_SET(_codebug_commands[0], &fs); + FD_SET(_codebug_stdout[0], &fs); + + fflush(NULL); + select(max_n + 1, &fs, NULL, NULL, NULL); + + if (FD_ISSET(_codebug_commands[0], &fs)) { + char buf[CODEBUG_BUFSIZE]; + int br; + do { + br = read(_codebug_commands[0], buf, CODEBUG_BUFSIZE); + if (br > 0) + write(_codebug_stdin[1], buf, br); + } while (br == CODEBUG_BUFSIZE); + } + + if (FD_ISSET(_codebug_stdout[0], &fs)) + _print_colored(_codebug_stdout[0]); + + } + } else { + /* child */ + close(_codebug_stdin[1]); + close(_codebug_stdout[0]); + + /* Re-associate stdin, stdout, stderr with i/o pipes */ + dup2(_codebug_stdin[0], 0); + dup2(_codebug_stdout[1], 1); + dup2(_codebug_stdout[1], 2); + + printf("Calling '%s' on data directory '%s'...\n", path, datapath); + execlp(path, path, "-D", "-gnull", "-Pnull", "-Onull", "-Mmt32gm", + "--disable-readline", "-d", datapath, NULL); + perror("execlp of FreeSCI"); + exit(1); + } +} + +static int +c_codebug(state_t *s) +{ + char *path = cmd_params[0].str; + + if (_codebug_pid) { + sciprintf("Already codebugging!\n"); + return 1; + } + + pipe(_codebug_commands); + _codebug_pid = fork(); + + if (!_codebug_pid) + do_codebug(path, s->resource_dir); /* Do codebugging in a child process */ + else { + close(_codebug_commands[0]); + sleep(1); /* Yield to the scheduler, at least a bit */ + atexit(_codebug_kill_children); + } + return 0; +} +#endif + +static int +c_backtrace(state_t *s) +{ + int i; + + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + sciprintf("Call stack (current base: 0x%x):\n", s->execution_stack_base); + for (i = 0; i <= s->execution_stack_pos; i++) { + exec_stack_t *call = &(s->execution_stack[i]); + const char *objname = obj_get_name(s, call->sendp); + int paramc, totalparamc; + + switch (call->type) { + + case EXEC_STACK_TYPE_CALL: {/* Normal function */ + sciprintf(" %x:[%x] %s::%s(", i, call->origin, + objname, (call->selector == -1)? "": + selector_name(s,call->selector)); + } + break; + + case EXEC_STACK_TYPE_KERNEL: /* Kernel function */ + sciprintf(" %x:[%x] k%s(", i, call->origin, s->kernel_names[-(call->selector)-42]); + break; + + case EXEC_STACK_TYPE_VARSELECTOR: + sciprintf(" %x:[%x] vs%s %s::%s (", i, call->origin, (call->argc)? "write" : "read", + objname, s->selector_names[call->selector]); + break; + } /* switch */ + + totalparamc = call->argc; + + if (totalparamc > 16) + totalparamc = 16; + + for (paramc = 1; paramc <= totalparamc; paramc++) { + sciprintf(PREG, PRINT_REG(call->variables_argp[paramc])); + + if (paramc < call->argc) + sciprintf(", "); + } + + if (call->argc > 16) + sciprintf("..."); + + sciprintf(")\n obj@"PREG, + PRINT_REG(call->objp)); + if (call->type == EXEC_STACK_TYPE_CALL) { + sciprintf(" pc="PREG, + PRINT_REG(call->addr.pc)); + if (call->sp == CALL_SP_CARRY) + sciprintf(" sp,fp:carry"); + else { + sciprintf(" sp="PSTK, + PRINT_STK(call->sp)); + sciprintf(" fp="PSTK, + PRINT_STK(call->fp)); + } + } else + sciprintf(" pc:none"); + + sciprintf(" argp:"PSTK, PRINT_STK(call->variables_argp)); + if (call->type == EXEC_STACK_TYPE_CALL) + sciprintf(" script: %d", s->seg_manager.heap[call->addr.pc.segment]->data.script.nr); + sciprintf("\n"); + } + return 0; +} + +static int +c_redraw_screen(state_t *s) +{ + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + s->visual->draw(GFXW(s->visual), gfx_point(0,0)); + gfxop_update_box(s->gfx_state, gfx_rect(0, 0, 320, 200)); + gfxop_update(s->gfx_state); + gfxop_usleep(s->gfx_state, 10); + + return 0; +} + + +static int +c_clear_screen(state_t *s) +{ + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + gfxop_clear_box(s->gfx_state, gfx_rect(0, 0, 320, 200)); + gfxop_update_box(s->gfx_state, gfx_rect(0, 0, 320, 200)); + return 0; +} + +static int +c_visible_map(state_t *s) +{ + if (!s) { + sciprintf("Not in debug state\n"); + return 1; + } +WARNING(fixme!) +#if 0 + if (s->onscreen_console) + con_restore_screen(s, s->osc_backup); + + if (cmd_params[0].val <= 3) s->pic_visible_map = cmd_params[0].val; + c_redraw_screen(s); + + if (s->onscreen_console) + s->osc_backup = con_backup_screen(s); +#endif + return 0; +} + + +static int +c_gfx_current_port(state_t *s) +{ + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + if (!s->port) + sciprintf("none.\n"); + else + sciprintf("%d\n", s->port->ID); + return 0; +} + +static int +c_gfx_print_port(state_t *s) +{ + gfxw_port_t *port; + + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + port = s->port; + + if (cmd_paramlength > 0) { + if (s->visual) { + port = gfxw_find_port(s->visual, cmd_params[0].val); + } else { + sciprintf("visual is uninitialized.\n"); + return 1; + } + } + + if (port) + port->print(GFXW(port), 0); + else + sciprintf("No such port.\n"); + + return 0; +} + +static int +c_gfx_priority(state_t *s) +{ + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + if (cmd_paramlength) { + int zone = cmd_params[0].val; + if (zone < 0) + zone = 0; + if (zone > 15) zone = 15; + + sciprintf("Zone %x starts at y=%d\n", zone, PRIORITY_BAND_FIRST(zone)); + } else { + sciprintf("Priority bands start at y=%d\nThey end at y=%d\n", s->priority_first, s->priority_last); + } + + return 0; +} + +static int +c_gfx_print_visual(state_t *s) +{ + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + if (s->visual) + s->visual->print(GFXW(s->visual), 0); + else + sciprintf("visual is uninitialized.\n"); + + return 0; +} + +static int +c_gfx_print_dynviews(state_t *s) +{ + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + if (!s->dyn_views) + sciprintf("No dynview list active.\n"); + else + s->dyn_views->print(GFXW(s->dyn_views), 0); + + return 0; +} + +static int +c_gfx_print_dropviews(state_t *s) +{ + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + if (!s->drop_views) + sciprintf("No dropped dynview list active.\n"); + else + s->drop_views->print(GFXW(s->drop_views), 0); + + return 0; +} + +static int +c_gfx_drawpic(state_t *s) +{ + int flags = 1, default_palette = 0; + + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + if (cmd_paramlength > 1) { + default_palette = cmd_params[1].val; + + if (cmd_paramlength > 2) + flags = cmd_params[2].val; + } + + gfxop_new_pic(s->gfx_state, cmd_params[0].val, flags, default_palette); + gfxop_clear_box(s->gfx_state, gfx_rect(0, 0, 320, 200)); + gfxop_update(s->gfx_state); + gfxop_usleep(s->gfx_state, 0); + return 0; +} + +#ifdef GFXW_DEBUG_WIDGETS +extern gfxw_widget_t *debug_widgets[]; +extern int debug_widget_pos; + +static int +c_gfx_print_widget(state_t *s) +{ + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + if (cmd_paramlength) { + unsigned int i; + for (i = 0; i < cmd_paramlength ; i++) { + int widget_nr = cmd_params[i].val; + + sciprintf("===== Widget #%d:\n", widget_nr); + debug_widgets[widget_nr]->print(debug_widgets[widget_nr], 0); + } + + } else if(debug_widget_pos>1) + sciprintf("Widgets 0-%d are active\n", debug_widget_pos-1); + else if (debug_widget_pos == 1) + sciprintf("Widget 0 is active\n"); + else + sciprintf("No widgets are active\n"); + + return 0; +} +#endif + +static int +c_gfx_show_map(state_t *s) +{ + int map = cmd_params[0].val; + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen); + + switch (map) { + case 0: + s->visual->add_dirty_abs(GFXWC(s->visual), gfx_rect(0, 0, 320, 200), 0); + s->visual->draw(GFXW(s->visual), gfx_point(0, 0)); + break; + + case 1: + gfx_xlate_pixmap(s->gfx_state->pic->priority_map, s->gfx_state->driver->mode, GFX_XLATE_FILTER_NONE); + gfxop_draw_pixmap(s->gfx_state, s->gfx_state->pic->priority_map, gfx_rect(0, 0, 320, 200), gfx_point(0, 0)); + break; + + case 2: + gfx_xlate_pixmap(s->gfx_state->control_map, s->gfx_state->driver->mode, GFX_XLATE_FILTER_NONE); + gfxop_draw_pixmap(s->gfx_state, s->gfx_state->control_map, gfx_rect(0, 0, 320, 200), gfx_point(0, 0)); + break; + + default: + sciprintf("Map %d is not available.\n", map); + return 1; + } + + gfxop_update(s->gfx_state); + return 0; +} + + +static int +c_gfx_draw_cel(state_t *s) +{ + int view = cmd_params[0].val; + int loop = cmd_params[1].val; + int cel = cmd_params[2].val; + int palette = cmd_params[3].val; + if (!s) { + sciprintf("Not in debug state!\n"); + return 1; + } + + gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen); + gfxop_draw_cel(s->gfx_state, view, loop, cel, gfx_point(160, 100), + s->ega_colors[0], palette); + gfxop_update(s->gfx_state); + + return 0; +} + +static int +c_gfx_fill_screen(state_t *s) +{ + int col = cmd_params[0].val; + + if (!s) { + sciprintf("Not in debug state!\n"); + return 1; + } + + if (col < 0 || col > 15) + col = 0; + + gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen); + gfxop_fill_box(s->gfx_state, gfx_rect_fullscreen, s->ega_colors[col]); + gfxop_update(s->gfx_state); + + return 0; +} + +static int +c_gfx_draw_rect(state_t *s) +{ + int col = cmd_params[4].val; + + if (!s) { + sciprintf("Not in debug state!\n"); + return 1; + } + + if (col < 0 || col > 15) + col = 0; + + gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen); + gfxop_fill_box(s->gfx_state, gfx_rect(cmd_params[0].val, cmd_params[1].val, cmd_params[2].val, cmd_params[3].val), s->ega_colors[col]); + gfxop_update(s->gfx_state); + + return 0; +} + +static int +c_gfx_propagate_rect(state_t *s) +{ + int map = cmd_params[4].val; + rect_t rect; + + if (!s) { + sciprintf("Not in debug state!\n"); + return 1; + } + + if (map < 0 || map > 1) + map = 0; + + gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen); + + rect = gfx_rect(cmd_params[0].val, + cmd_params[1].val, + cmd_params[2].val, + cmd_params[3].val); + + if (map == 1) + gfxop_clear_box(s->gfx_state, rect); + else + gfxop_update_box(s->gfx_state, rect); + gfxop_update(s->gfx_state); + gfxop_usleep(s->gfx_state, 10); + + return 0; +} + +#define GETRECT(ll, rr, tt, bb) \ +ll = GET_SELECTOR(pos, ll); \ +rr = GET_SELECTOR(pos, rr); \ +tt = GET_SELECTOR(pos, tt); \ +bb = GET_SELECTOR(pos, bb); + +static int +c_gfx_draw_viewobj(state_t *s) +{ +#ifdef __GNUC__ +#warning "Re-implement con:gfx_draw_viewobj" +#endif +#if 0 + heap_ptr pos = (heap_ptr) (cmd_params[0].val); + int is_view; + int x, y, priority; + int nsLeft, nsRight, nsBottom, nsTop; + int brLeft, brRight, brBottom, brTop; + + if (!s) { + sciprintf("Not in debug state!\n"); + return 1; + } + + if ((pos < 4) || (pos > 0xfff0)) { + sciprintf("Invalid address.\n"); + return 1; + } + + if ((getInt16(s->heap + pos + SCRIPT_OBJECT_MAGIC_OFFSET)) != SCRIPT_OBJECT_MAGIC_NUMBER) { + sciprintf("Not an object.\n"); + return 0; + } + + + is_view = + (lookup_selector(s, pos, s->selector_map.x, NULL) == SELECTOR_VARIABLE) + && + (lookup_selector(s, pos, s->selector_map.brLeft, NULL) == SELECTOR_VARIABLE) + && + (lookup_selector(s, pos, s->selector_map.signal, NULL) == SELECTOR_VARIABLE) + && + (lookup_selector(s, pos, s->selector_map.nsTop, NULL) == SELECTOR_VARIABLE); + + if (!is_view) { + sciprintf("Not a dynamic View object.\n"); + return 0; + } + + x = GET_SELECTOR(pos, x); + y = GET_SELECTOR(pos, y); + priority = GET_SELECTOR(pos, priority); + GETRECT(brLeft, brRight, brBottom, brTop); + GETRECT(nsLeft, nsRight, nsBottom, nsTop); + gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen); + + brTop += 10; + brBottom += 10; + nsTop += 10; + nsBottom += 10; + + gfxop_fill_box(s->gfx_state, gfx_rect(nsLeft, nsTop, + nsRight - nsLeft + 1, + nsBottom - nsTop + 1), + s->ega_colors[2]); + + gfxop_fill_box(s->gfx_state, gfx_rect(brLeft, brTop, + brRight - brLeft + 1, + brBottom - brTop + 1), + s->ega_colors[1]); + + + gfxop_fill_box(s->gfx_state, gfx_rect(x-1, y-1, + 3, 3), + s->ega_colors[0]); + + gfxop_fill_box(s->gfx_state, gfx_rect(x-1, y, + 3, 1), + s->ega_colors[priority]); + + gfxop_fill_box(s->gfx_state, gfx_rect(x, y-1, + 1, 3), + s->ega_colors[priority]); + + gfxop_update(s->gfx_state); + + return 0; +#endif +} + + +static int +c_gfx_flush_resources(state_t *s) +{ + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + gfxop_set_pointer_cursor(s->gfx_state, GFXOP_NO_POINTER); + sciprintf("Flushing resources...\n"); + s->visual->widfree(GFXW(s->visual)); + gfxr_free_all_resources(s->gfx_state->driver, s->gfx_state->resstate); + s->visual = NULL; + return 0; +} + +static int +c_gfx_update_zone(state_t *s) +{ + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + return s->gfx_state->driver->update(s->gfx_state->driver, + gfx_rect(cmd_params[0].val, + cmd_params[1].val, + cmd_params[2].val, + cmd_params[3].val), + gfx_point(cmd_params[0].val, + cmd_params[1].val), + GFX_BUFFER_FRONT + ); + +} + +static int +c_disasm_addr(state_t *s) +{ + reg_t vpc = cmd_params[0].reg; + int op_count = 1; + int do_bwc = 0; + int do_bytes = 0; + int i; + int invalid = 0; + int size; + sm_dereference(&s->seg_manager, vpc, &size); + size += vpc.offset; /* total segment size */ + + for (i = 1; i < cmd_paramlength; i++) { + if (!strcasecmp(cmd_params[i].str, "bwt")) + do_bwc = 1; + else if (!strcasecmp(cmd_params[i].str, "bc")) + do_bytes = 1; + else if (toupper(cmd_params[i].str[0]) == 'C') + op_count = atoi(cmd_params[i].str + 1); + else { + invalid = 1; + sciprintf("Invalid option '%s'\n", cmd_params[i].str); + } + } + + if (invalid || op_count < 0) + return invalid; + + do { + vpc = disassemble(s, vpc, do_bwc, do_bytes); + } while ((vpc.offset > 0) && (vpc.offset + 6 < size) && (--op_count)); + return 0; +} + + +static int +c_disasm(state_t *s) +{ + object_t *obj = obj_get(s, cmd_params[0].reg); + int selector_id = script_find_selector(s, cmd_params[1].str); + reg_t addr; + + if (!obj) { + sciprintf("Not an object."); + return 1; + } + + if (selector_id < 0) { + sciprintf("Not a valid selector name."); + return 1; + } + + if (lookup_selector(s, cmd_params[0].reg, selector_id, NULL, &addr) != SELECTOR_METHOD) { + sciprintf("Not a method."); + return 1; + } + + do { + addr = disassemble(s, addr, 0, 0); + } while (addr.offset > 0); + + return 0; +} + +static int +c_sg(state_t *s) +{ + _debug_seeking = _DEBUG_SEEK_GLOBAL; + _debug_seek_special = cmd_params[0].val; + _debugstate_valid = 0; + + return 0; +} + + +static int +c_snk(state_t *s) +{ + int callk_index; + char *endptr; + + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + if (cmd_paramlength > 0) { + /* Try to convert the parameter to a number. If the conversion stops + before end of string, assume that the parameter is a function name + and scan the function table to find out the index. */ + callk_index = strtoul (cmd_params [0].str, &endptr, 0); + if (*endptr != '\0') + { + int i; + + callk_index = -1; + for (i = 0; i < s->kernel_names_nr; i++) + if (!strcmp (cmd_params [0].str, s->kernel_names [i])) + { + callk_index = i; + break; + } + + if (callk_index == -1) + { + sciprintf ("Unknown kernel function '%s'\n", cmd_params [0].str); + return 1; + } + } + + _debug_seeking = _DEBUG_SEEK_SPECIAL_CALLK; + _debug_seek_special = callk_index; + _debugstate_valid = 0; + } else { + _debug_seeking = _DEBUG_SEEK_CALLK; + _debugstate_valid = 0; + } + return 0; +} + + +static int +c_sret(state_t *s) +{ + _debug_seeking = _DEBUG_SEEK_LEVEL_RET; + _debug_seek_level = s->execution_stack_pos; + _debugstate_valid = 0; + return 0; +} + +static int +c_go(state_t *s) +{ + _debug_seeking = 0; + _debugstate_valid = 0; + script_debug_flag = 0; + return 0; +} + + +static int +c_set_acc(state_t *s) +{ + s->r_acc = cmd_params[0].reg; + return 0; +} + +static int +c_send(state_t *s) +{ + reg_t object = cmd_params[0].reg; + char *selector_name = cmd_params[1].str; + stack_ptr_t stackframe = s->execution_stack->sp; + int i, selector_id, selector_type; + exec_stack_t *xstack; + object_t *o; + reg_t *vptr; + reg_t fptr; + + selector_id = vocabulary_lookup_sname(s->selector_names, selector_name); + + if (selector_id < 0) + { + sciprintf("Unknown selector: \"%s\"\n", selector_name); + return 1; + } + + o = obj_get(s, object); + if (o == NULL) + { + sciprintf("Address \""PREG"\" is not an object\n", PRINT_REG(object)); + return 1; + } + + selector_type = lookup_selector(s, object, selector_id, &vptr, &fptr); + + if (selector_type == SELECTOR_NONE) + { + sciprintf("Object does not support selector: \"%s\"\n", selector_name); + return 1; + } + + stackframe[0] = make_reg(0, selector_id); + stackframe[1] = make_reg(0, cmd_paramlength - 2); + + for (i=2; iexecution_stack->sp + cmd_paramlength, object, + cmd_paramlength - 2, s->execution_stack->sp - 1, 0, object, + s->execution_stack_pos, SCI_XS_CALLEE_LOCALS); + xstack->selector = selector_id; + xstack->type = selector_type == SELECTOR_VARIABLE ? EXEC_STACK_TYPE_VARSELECTOR : + EXEC_STACK_TYPE_CALL; + + /* Now commit the actual function: */ + xstack = send_selector(s, object, object, + stackframe, cmd_paramlength - 2, stackframe); + + xstack->sp += cmd_paramlength; + xstack->fp += cmd_paramlength; + + s->execution_stack_pos_changed = 1; + + return 0; +} + +static int +c_resource_id(state_t *s) +{ + int id = cmd_params[0].val; + + sciprintf("%s.%d (0x%x)\n", sci_resource_types[id >> 11], id &0x7ff, id & 0x7ff); + return 0; +} + +static int +c_listclones(state_t *s) +{ + /* + int i, j = 0; + sciprintf("Listing all logged clones:\n"); + + for (i = 0; i < SCRIPT_MAX_CLONES; i++) + if (s->clone_list[i]) { + sciprintf(" Clone at %04x\n", s->clone_list[i]); + j++; + } + + sciprintf("Total of %d clones.\n", j); + */ + sciprintf("This function is temporarily disabled.\n"); + return 0; +} + + +typedef struct { + const char *name; + const char option; + unsigned int flag; +} generic_config_flag_t; + + +static void +handle_config_update(const generic_config_flag_t *flags_list, int flags_nr, + const char *subsystem, + int *active_options_p, + char *changestring /* or NULL to display*/) +{ + if (!changestring) { + int j; + + sciprintf("Logging in %s:\n", subsystem); + if (!(*active_options_p)) + sciprintf(" (nothing)\n"); + + for (j = 0; j < flags_nr; j++) + if (*active_options_p + & flags_list[j].flag) { + sciprintf(" - %s (%c)\n", + flags_list[j].name, + flags_list[j].option); + } + + } else { + int mode; + int j = 0; + int flags = 0; + + if (changestring[0] == '-') + mode = 0; + else if (changestring[0] == '+') + mode = 1; + else { + sciprintf("Mode spec must start with '+' or '-' in '%s'\n", changestring); + return; + } + + while (changestring[++j]) { + int k; + int flag = 0; + + if (changestring[j] == '*') + flags = ~0; /* Everything */ + else + for (k = 0; !flag && k < flags_nr; k++) + if (flags_list[k].option + == changestring[j]) + flag = flags_list[k].flag; + + if (!flag) { + sciprintf("Invalid/unknown mode flag '%c'\n", + changestring[j]); + return; + } + flags |= flag; + } + + if (mode) /* + */ + *active_options_p |= flags; + else /* - */ + *active_options_p &= ~flags; + } + +} + +static int +c_handle_config_update(const generic_config_flag_t *flags, int flags_nr, + const char *subsystem, int *active_options_p) +{ + int i; + + if (!_debugstate_valid) { + sciprintf("Not in debug state\n"); + return 1; + } + + if (cmd_paramlength == 0) + handle_config_update(flags, flags_nr, + subsystem, active_options_p, + 0); + + for (i = 0; i < cmd_paramlength; i++) + handle_config_update(flags, flags_nr, + subsystem, active_options_p, + cmd_params[i].str); + return 0; +} + +const generic_config_flag_t SCIk_Debug_Names[SCIk_DEBUG_MODES] = { + {"Stubs", 'u', (1 << SCIkSTUB_NR)}, + {"Lists and nodes", 'l', (1 << 1)}, + {"Graphics", 'g', (1 << 2)}, + {"Character handling", 'c', (1 << 3)}, + {"Memory management", 'm', (1 << 4)}, + {"Function parameter checks", 'f', (1 << SCIkFUNCCHK_NR)}, + {"Bresenham algorithms", 'b', (1 << 6)}, + {"Audio subsystem", 'a', (1 << SCIkSOUNDCHK_NR)}, + {"System graphics driver", 'd', (1 << SCIkGFXDRIVER_NR)}, + {"Base setter results", 's', (1 << SCIkBASESETTER_NR)}, + {"Parser", 'p', (1 << SCIkPARSER_NR)}, + {"Menu handling", 'M', (1 << 11)}, + {"Said specs", 'S', (1 << 12)}, + {"File I/O", 'F', (1 << 13)}, + {"Time", 't', (1 << 14)}, + {"Room numbers", 'r', (1 << 15)}, + {"FreeSCI 0.3.3 kernel emulation", 'e', (1 << 16)}, + {"Pathfinding", 'P', (1 << SCIkAVOIDPATH_NR)} +} ; + + +void +set_debug_mode (struct _state *s, int mode, const char *areas) +{ + char *param = (char*)sci_malloc(strlen(areas) + 2); + + param[0] = (mode)? '+' : '-'; + strcpy(param + 1, areas); + + handle_config_update(SCIk_Debug_Names, SCIk_DEBUG_MODES, + "VM and kernel", + (int *) &(s->debug_mode), + param); + + sci_free(param); +} + +int +c_debuglog(state_t *s) +{ + return c_handle_config_update(SCIk_Debug_Names, SCIk_DEBUG_MODES, + "VM and kernel", + (int *) &(s->debug_mode)); +} + +#define SFX_DEBUG_MODES 2 +#define FROBNICATE_HANDLE(reg) ((reg).segment << 16 | (reg).offset) + +static int +c_sfx_debuglog(state_t *s) +{ + const generic_config_flag_t sfx_debug_modes[SFX_DEBUG_MODES] = { + {"Song activation/deactivation", 's', SFX_DEBUG_SONGS}, + {"Song cue polling and delivery", 'c', SFX_DEBUG_CUES} + }; + + return c_handle_config_update(sfx_debug_modes, SFX_DEBUG_MODES, + "sound subsystem", + (int *) &(s->sound.debug)); +} + +static int +c_sfx_remove(state_t *s) +{ + reg_t id = cmd_params[0].reg; + int handle = FROBNICATE_HANDLE(id); + + if (id.segment) { + sfx_song_set_status(&s->sound, + handle, SOUND_STATUS_STOPPED); + sfx_remove_song(&s->sound, handle); + PUT_SEL32V(id, signal, -1); + PUT_SEL32V(id, nodePtr, 0); + PUT_SEL32V(id, handle, 0); + } + + return 0; +} + +#define GFX_DEBUG_MODES 4 + +int +c_gfx_debuglog(state_t *s) +{ + gfx_driver_t *drv = s->gfx_state->driver; + const generic_config_flag_t gfx_debug_modes[GFX_DEBUG_MODES] = { + { "Mouse Pointer", 'p', GFX_DEBUG_POINTER}, + { "Updates", 'u', GFX_DEBUG_UPDATES}, + { "Pixmap operations", 'x', GFX_DEBUG_PIXMAPS}, + { "Basic operations", 'b', GFX_DEBUG_BASIC}, + }; + + return c_handle_config_update(gfx_debug_modes, GFX_DEBUG_MODES, + "graphics subsystem", + (int *) &(drv->debug_flags)); + +} + +int +c_dump_words(state_t *s) +{ + int i; + + if (!s) { + sciprintf("Not in debug state\n"); + return 1; + } + + if (!s->parser_words) { + sciprintf("No words.\n"); + return 0; + } + + for (i = 0; i < s->parser_words_nr; i++) { + word_t *word = s->parser_words[i]; + sciprintf("%s: C %03x G %03x\n", word->word, word->w_class, word->group); + } + sciprintf("%d words\n", s->parser_words_nr); + + return 0; +} + +int +c_simkey(state_t *s) +{ + _kdebug_cheap_event_hack = cmd_params[0].val; + return 0; +} + +static int +c_is_sample(state_t *s) +{ + resource_t *song = scir_find_resource(s->resmgr, + sci_sound, + cmd_params[0].val, + 0); + song_iterator_t *songit; + sfx_pcm_feed_t *data; + + if (!song) { + sciprintf("Not a sound resource.\n"); + return 1; + } + + songit = songit_new(song->data, song->size, SCI_SONG_ITERATOR_TYPE_SCI0, + 0xcaffe /* What do I care about the ID? */); + + if (!songit) { + sciprintf("Error-- Could not convert to song iterator\n"); + return 1; + } + + if ((data = songit->get_pcm_feed(songit))) { + sciprintf("\nIs sample (encoding %dHz/%s/%04x).\n", + data->conf.rate, (data->conf.stereo)? + ((data->conf.stereo == SFX_PCM_STEREO_LR)? + "stereo-LR" : "stereo-RL") : "mono", + data->conf.format); + data->destroy(data); + } else + sciprintf("Valid song, but not a sample.\n"); + + songit_free(songit); + + return 0; +} + +int +c_simsoundcue(state_t *s) +{ + _kdebug_cheap_soundcue_hack = cmd_params[0].val; + return 0; +} + + +#define ASSERT_PARAMS(number) \ + if (cmd_paramlength <= number) {\ + sciprintf("Operation '%s' needs %d parameters\n", op, number); \ + return 1;\ + } + + + +#define GETRECT(ll, rr, tt, bb) \ +ll = GET_SELECTOR(pos, ll); \ +rr = GET_SELECTOR(pos, rr); \ +tt = GET_SELECTOR(pos, tt); \ +bb = GET_SELECTOR(pos, bb); + +#if 0 +#ifdef __GNUC__ +#warning "Re-implement viewobjinfo" +#endif +static void +viewobjinfo(state_t *s, heap_ptr pos) +{ + + char *signals[16] = { + "stop_update", + "updated", + "no_update", + "hidden", + "fixed_priority", + "always_update", + "force_update", + "remove", + "frozen", + "is_extra", + "hit_obstacle", + "doesnt_turn", + "no_cycler", + "ignore_horizon", + "ignore_actor", + "dispose!" + }; + + int x, y, z, priority; + int cel, loop, view, signal; + int nsLeft, nsRight, nsBottom, nsTop; + int lsLeft, lsRight, lsBottom, lsTop; + int brLeft, brRight, brBottom, brTop; + int i; + int have_rects = 0; + abs_rect_t nsrect, nsrect_clipped, brrect; + + if (lookup_selector(s, pos, s->selector_map.nsBottom, NULL) == SELECTOR_VARIABLE) { + GETRECT(nsLeft, nsRight, nsBottom, nsTop); + GETRECT(lsLeft, lsRight, lsBottom, lsTop); + GETRECT(brLeft, brRight, brBottom, brTop); + have_rects = 1; + } + + GETRECT(view, loop, signal, cel); + + sciprintf("\n-- View information:\ncel %d/%d/%d at ", view, loop, cel); + + x = GET_SELECTOR(pos, x); + y = GET_SELECTOR(pos, y); + priority = GET_SELECTOR(pos, priority); + if (s->selector_map.z > 0) { + z = GET_SELECTOR(pos, z); + sciprintf("(%d,%d,%d)\n", x, y, z); + } else sciprintf("(%d,%d)\n", x, y); + + if (priority == -1) + sciprintf("No priority.\n\n"); + else + sciprintf("Priority = %d (band starts at %d)\n\n", + priority, PRIORITY_BAND_FIRST(priority)); + + if (have_rects) { + sciprintf("nsRect: [%d..%d]x[%d..%d]\n", nsLeft, nsRight, nsTop, nsBottom); + sciprintf("lsRect: [%d..%d]x[%d..%d]\n", lsLeft, lsRight, lsTop, lsBottom); + sciprintf("brRect: [%d..%d]x[%d..%d]\n", brLeft, brRight, brTop, brBottom); + } + + nsrect = get_nsrect(s, pos, 0); + nsrect_clipped = get_nsrect(s, pos, 1); + brrect = set_base(s, pos); + sciprintf("new nsRect: [%d..%d]x[%d..%d]\n", nsrect.x, nsrect.xend, + nsrect.y, nsrect.yend); + sciprintf("new clipped nsRect: [%d..%d]x[%d..%d]\n", nsrect_clipped.x, + nsrect_clipped.xend, nsrect_clipped.y, nsrect_clipped.yend); + sciprintf("new brRect: [%d..%d]x[%d..%d]\n", brrect.x, brrect.xend, + brrect.y, brrect.yend); + sciprintf("\n signals = %04x:\n", signal); + + for (i = 0; i < 16; i++) + if (signal & (1 << i)) + sciprintf(" %04x: %s\n", 1 << i, signals[i]); +} +#endif +#undef GETRECT + + +int +objinfo(state_t *s, reg_t pos) +{ + object_t *obj = obj_get(s, pos); + object_t *var_container = obj; + int i; + + sciprintf("["PREG"]: ", PRINT_REG(pos)); + if (!obj) { + sciprintf("Not an object."); + return 1; + } + + print_obj_head(s, obj); + + if (!(obj->variables[SCRIPT_INFO_SELECTOR].offset & SCRIPT_INFO_CLASS)) + var_container = obj_get(s, obj->variables[SCRIPT_SUPERCLASS_SELECTOR]); + sciprintf(" -- member variables:\n"); + for (i = 0; i < obj->variables_nr; i++) { + + sciprintf(" "); + if (i < var_container->variable_names_nr) + sciprintf("[%03x] %s = ", VM_OBJECT_GET_VARSELECTOR(var_container, i), + selector_name(s, VM_OBJECT_GET_VARSELECTOR(var_container, i))); + else + sciprintf("p#%x = ", i); + + sciprintf(PREG"\n", PRINT_REG(obj->variables[i])); + } + sciprintf(" -- methods:\n"); + for (i = 0; i < obj->methods_nr; i++) { + reg_t fptr = VM_OBJECT_READ_FUNCTION(obj, i); + sciprintf(" [%03x] %s = "PREG"\n", + VM_OBJECT_GET_FUNCSELECTOR(obj, i), + selector_name(s, VM_OBJECT_GET_FUNCSELECTOR(obj, i)), + PRINT_REG(fptr)); + } + if (s->seg_manager.heap[pos.segment]->type==MEM_OBJ_SCRIPT) + sciprintf("\nOwner script:\t%d\n", s->seg_manager.heap[pos.segment]->data.script.nr); + return 0; +} + +int +c_vo(state_t *s) +{ + return objinfo(s, cmd_params[0].reg); +} + +int +c_obj(state_t *s) +{ + return objinfo(s, *p_objp); +} + +int +c_accobj(state_t *s) +{ + return objinfo(s, s->r_acc); +} + +int +c_shownode(state_t *s) +{ + reg_t addr = cmd_params[0].reg; + + return show_node(s, addr); +} + +/*** Breakpoint commands ***/ + +static breakpoint_t * +bp_alloc(state_t *s) +{ + breakpoint_t *bp; + + if (s->bp_list) + { + bp = s->bp_list; + while (bp->next) + bp = bp->next; + bp->next = (breakpoint_t *) sci_malloc (sizeof (breakpoint_t)); + bp = bp->next; + } + else { + s->bp_list = (breakpoint_t *) sci_malloc (sizeof (breakpoint_t)); + bp = s->bp_list; + } + + bp->next = NULL; + + return bp; +} + +int +c_bpx(state_t *s) +{ + breakpoint_t *bp; + + /* Note: We can set a breakpoint on a method that has not been loaded yet. + Thus, we can't check whether the command argument is a valid method name. + A breakpoint set on an invalid method name will just never trigger. */ + + bp=bp_alloc (s); + + bp->type = BREAK_SELECTOR; + bp->data.name = (char*)sci_malloc (strlen (cmd_params [0].str)+1); + strcpy (bp->data.name, cmd_params [0].str); + s->have_bp |= BREAK_SELECTOR; + + return 0; +} + +int +c_bpe(state_t *s) +{ + breakpoint_t *bp; + + bp=bp_alloc (s); + + bp->type = BREAK_EXPORT; + bp->data.address = (cmd_params [0].val << 16 | cmd_params [1].val); + s->have_bp |= BREAK_EXPORT; + + return 0; +} + +int +c_bplist(state_t *s) +{ + breakpoint_t *bp; + int i = 0; + long bpdata; + + bp = s->bp_list; + while (bp) + { + sciprintf (" #%i: ", i); + switch (bp->type) + { + case BREAK_SELECTOR: + sciprintf ("Execute %s\n", bp->data.name); + break; + case BREAK_EXPORT: + bpdata = bp->data.address; + sciprintf ("Execute script %d, export %d\n", bpdata >> 16, bpdata & 0xFFFF); + break; + } + + bp = bp->next; + i++; + } + + return 0; +} + +int c_bpdel(state_t *s) +{ + breakpoint_t *bp, *bp_next, *bp_prev; + int i = 0, found = 0; + int type; + + /* Find breakpoint with given index */ + bp_prev = NULL; + bp = s->bp_list; + while (bp && i < cmd_params [0].val) + { + bp_prev = bp; + bp = bp->next; + i++; + } + if (!bp) + { + sciprintf ("Invalid breakpoint index %i\n", cmd_params [0].val); + return 1; + } + + /* Delete it */ + bp_next = bp->next; + type = bp->type; + if (type == BREAK_SELECTOR) sci_free (bp->data.name); + free (bp); + if (bp_prev) + bp_prev->next = bp_next; + else + s->bp_list = bp_next; + + /* Check if there are more breakpoints of the same type. If not, clear + the respective bit in s->have_bp. */ + for (bp = s->bp_list; bp; bp=bp->next) + if (bp->type == type) + { + found = 1; + break; + } + + if (!found) s->have_bp &= ~type; + + return 0; +} + +int +c_gnf(state_t *s) +{ + if (!s) { + sciprintf("Not in debug state\n"); + return 1; + } + + vocab_gnf_dump(s->parser_branches, s->parser_branches_nr); + return 0; +} + +int +c_se(state_t *s) +{ + stop_on_event=1; + _debugstate_valid = script_debug_flag = script_error_flag = 0; + return 0; +} + +int +c_type(state_t *s) +{ + int t = determine_reg_type(s, cmd_params[0].reg, 1); + int invalid = t & KSIG_INVALID; + + switch (t & ~KSIG_INVALID) { + + case 0: + sciprintf("Invalid"); + break; + + case KSIG_LIST: + sciprintf("List"); + break; + + case KSIG_OBJECT: + sciprintf("Object"); + break; + + case KSIG_REF: + sciprintf("Reference"); + break; + + case KSIG_ARITHMETIC: + sciprintf("Arithmetic"); + break; + + default: + sciprintf("Erroneous unknown type %02x(%d decimal)\n", t, t); + + } + + sciprintf("%s\n", invalid ? " (invalid)" : ""); +} + +int +c_statusbar(state_t *s) +{ + if (!s) { + sciprintf("Not in debug state\n"); + return 1; + } + + s->titlebar_port->color=s->ega_colors[cmd_params[0].val]; + s->titlebar_port->bgcolor=s->ega_colors[cmd_params[1].val]; + + s->status_bar_foreground=cmd_params[0].val; + s->status_bar_background=cmd_params[1].val; + + sciw_set_status_bar(s, s->titlebar_port, s->status_bar_text, + s->status_bar_foreground, + s->status_bar_background); + gfxop_update(s->gfx_state); + return 0; +} + +int +c_sci_version(state_t *s) +{ + if (!s) { + sciprintf("Not in debug state\n"); + return 1; + } + + sciprintf("Emulating SCI version %d.%03d.%03d\n", + SCI_VERSION_MAJOR(s->version), + SCI_VERSION_MINOR(s->version), + SCI_VERSION_PATCHLEVEL(s->version)); + + return 0; +} + + +int +c_sleep(state_t *s) +{ + sleep(cmd_params[0].val); + return 0; +} + +static void +_print_address(void * _, reg_t addr) +{ + if (addr.segment) + sciprintf(" "PREG"\n", PRINT_REG(addr)); +} + +#define GET_SEG_INTERFACE(seg_id) \ + seg_interface_t * seg_interface = get_seg_interface(&(s->seg_manager), seg_id); \ + if (!seg_interface) { \ + sciprintf("Unknown segment : %x\n", seg_id); \ + return 1; \ + } + +static int +c_gc_show_reachable(state_t *s) +{ + reg_t addr = cmd_params[0].reg; + + GET_SEG_INTERFACE(addr.segment); + + sciprintf("Reachable from "PREG":\n", PRINT_REG(addr)); + seg_interface->list_all_outgoing_references(seg_interface, s, addr, NULL, _print_address); + + seg_interface->deallocate_self(seg_interface); + return 0; +} + +static int +c_gc_show_freeable(state_t *s) +{ + reg_t addr = cmd_params[0].reg; + + GET_SEG_INTERFACE(addr.segment); + + sciprintf("Freeable in segment %04x:\n", addr.segment); + seg_interface->list_all_deallocatable(seg_interface, NULL, _print_address); + + seg_interface->deallocate_self(seg_interface); + return 0; +} + +static int +c_gc_normalise(state_t *s) +{ + reg_t addr = cmd_params[0].reg; + + GET_SEG_INTERFACE(addr.segment); + + addr = seg_interface->find_canonic_address(seg_interface, addr); + sciprintf(" "PREG"\n", PRINT_REG(addr)); + + seg_interface->deallocate_self(seg_interface); + return 0; + +} + +static int +c_gc(state_t *s) +{ + run_gc(s); + return 0; +} + +static void +print_all_of_them(void *_1, reg_t reg, int _2) +{ + sciprintf(" - "PREG"\n", PRINT_REG(reg)); +} + +static int +c_gc_list_reachable(state_t *s) +{ + reg_t_hash_map_ptr use_map = find_all_used_references(s); + + sciprintf("Reachable references (normalised):\n"); + apply_to_reg_t_hash_map(use_map, NULL, print_all_of_them); + + free_reg_t_hash_map (use_map); +} + + +void +script_debug(state_t *s, reg_t *pc, stack_ptr_t *sp, stack_ptr_t *pp, reg_t *objp, + int *restadjust, + seg_id_t *segids, reg_t **variables, + reg_t **variables_base, int *variables_nr, + int bp) +{ + int have_windowed = s->gfx_state->driver->capabilities & GFX_CAPABILITY_WINDOWED; + static int last_step; + /* Do we support a separate console? */ + +#ifndef WANT_CONSOLE + int missing_tty = !isatty(0) || !isatty(1); + + if (!have_windowed || missing_tty) { + script_debug_flag = sci_debug_flags = 0; + + fprintf(stderr, "On-screen console disabled and "); + if (!have_windowed) + fprintf(stderr, "driver claims to be running fullscreen.\n"); + else + fprintf(stderr, "no terminal found.\n"); + + if (last_step == script_step_counter) + fprintf(stderr, "This error seems to be unrecoverable.\n"); + if (script_error_flag || script_step_counter == last_step) { + fprintf(stderr, "Aborting...\n"); + exit(1); + } else + fprintf(stderr, "Continuing...\n"); + last_step = script_step_counter; + return; + } +#endif + + + if (sci_debug_flags & _DEBUG_FLAG_LOGGING) { + int old_debugstate = _debugstate_valid; + + p_var_segs = segids; + p_vars = variables; + p_var_max = variables_nr; + p_var_base = variables_base; + p_pc = pc; + p_sp = sp; + p_pp = pp; + p_objp = objp; + p_restadjust = restadjust; + sciprintf("%d: acc="PREG" ", script_step_counter, PRINT_REG(s->r_acc)); + _debugstate_valid = 1; + disassemble(s, *pc, 0, 1); + if (_debug_seeking == _DEBUG_SEEK_GLOBAL) + sciprintf("Global %d (0x%x) = "PREG"\n", _debug_seek_special, + _debug_seek_special, PRINT_REG(s->script_000->locals_block->locals[_debug_seek_special])); + + _debugstate_valid = old_debugstate; + + if (!script_debug_flag) + return; + } + + if (_debug_seeking && !bp) { /* Are we looking for something special? */ + mem_obj_t *memobj = GET_SEGMENT(s->seg_manager, pc->segment, MEM_OBJ_SCRIPT); + + if (memobj) { + script_t *scr = &(memobj->data.script); + byte *code_buf = scr->buf; + int code_buf_size = scr->buf_size; + int opcode = pc->offset >= code_buf_size? 0 : code_buf[pc->offset]; + int op = opcode >> 1; + int paramb1 = pc->offset + 1 >= code_buf_size? 0 : code_buf[pc->offset + 1]; + int paramf1 = (opcode & 1)? paramb1 : + (pc->offset + 2 >= code_buf_size? 0 : getInt16(code_buf + pc->offset + 1)); + + switch (_debug_seeking) { + + case _DEBUG_SEEK_SPECIAL_CALLK: + if (paramb1 != _debug_seek_special) + return; + + case _DEBUG_SEEK_CALLK: { + if (op != op_callk) return; + break; + } + + case _DEBUG_SEEK_LEVEL_RET: { + if ((op != op_ret) || (_debug_seek_level < s->execution_stack_pos)) return; + break; + } + + case _DEBUG_SEEK_SO: + if (!REG_EQ(*pc,_debug_seek_reg) || + s->execution_stack_pos != _debug_seek_level) + return; + break; + + case _DEBUG_SEEK_GLOBAL: + + if (op < op_sag) + return; + if ((op & 0x3) > 1) + return; /* param or temp */ + if ((op & 0x3) && s->execution_stack[s->execution_stack_pos].local_segment > 0) + return; /* locals and not running in script.000 */ + if (paramf1 != _debug_seek_special) + return; /* CORRECT global? */ + + break; + + } /* switch(_debug_seeking) */ + + _debug_seeking = _DEBUG_SEEK_NOTHING; /* OK, found whatever we + ** were looking for */ + } + } /* if (_debug_seeking) */ + + + _debugstate_valid = (_debug_step_running == 0); + + if (_debugstate_valid) { + p_pc = pc; + p_sp = sp; + p_pp = pp; + p_objp = objp; + p_restadjust = restadjust; + p_var_segs = segids; + p_vars = variables; + p_var_max = variables_nr; + p_var_base = variables_base; + + c_debuginfo(s); + sciprintf("Step #%d\n", script_step_counter); + disassemble(s, *pc, 0, 1); + + if (_debug_commands_not_hooked) { + + _debug_commands_not_hooked = 0; + + con_hook_command(c_sfx_remove, "sfx_remove", "!a", + "Kills a playing sound."); + con_hook_command(c_debuginfo, "registers", "", "Displays all current register values"); + con_hook_command(c_vmvars, "vmvars", "!sia*", "Displays or changes variables in the VM\n\nFirst parameter is either g(lobal), l(ocal), t(emp) or p(aram).\nSecond parameter is the var number\nThird parameter (if specified) is the value to set the variable to"); + con_hook_command(c_sci_version, "sci_version", "", "Prints the SCI version currently being emulated"); + con_hook_command(c_vmvarlist, "vmvarlist", "!", "Displays the addresses of variables in the VM"); + con_hook_command(c_step, "s", "i*", "Executes one or several operations\n\nEXAMPLES\n\n" + " s 4\n\n Execute 4 commands\n\n s\n\n Execute next command"); +#ifdef __GNUC__ +#warning "Re-enable con:so hook" +#endif +#if 0 + con_hook_command(c_stepover, "so", "", "Executes one operation skipping over sends"); +#endif + con_hook_command(c_disasm_addr, "disasm-addr", "!as*", "Disassembles one or more commands\n\n" + "USAGE\n\n disasm-addr [startaddr] \n\n" + " Valid options are:\n" + " bwt : Print byte/word tag\n" + " c : Disassemble bytes\n" + " bc : Print bytecode\n\n"); + con_hook_command(c_disasm, "disasm", "!as", "Disassembles a method by name\n\nUSAGE\n\n disasm \n\n"); + con_hook_command(c_obj, "obj", "!", "Displays information about the\n currently active object/class.\n" + "\n\nSEE ALSO\n\n vo.1, accobj.1"); + con_hook_command(c_accobj, "accobj", "!", "Displays information about an\n object or class at the\n" + "address indexed by acc.\n\nSEE ALSO\n\n obj.1, vo.1"); + con_hook_command(c_classtable, "classtable", "", "Lists all available classes"); + con_hook_command(c_stack, "stack", "i", "Dumps the specified number of stack elements"); + con_hook_command(c_backtrace, "bt", "", "Dumps the send/self/super/call/calle/callb stack"); + con_hook_command(c_snk, "snk", "s*", "Steps forward until it hits the next\n callk operation.\n" + " If invoked with a parameter, it will\n look for that specific callk.\n"); + con_hook_command(c_se, "se", "", "Steps forward until an SCI event is received.\n"); + con_hook_command(c_listclones, "clonetable", "", "Lists all registered clones"); + con_hook_command(c_set_acc, "set_acc", "!a", "Sets the accumulator"); + con_hook_command(c_send, "send", "!asa*", "Sends a message to an object\nExample: send ?fooScript cue"); + con_hook_command(c_sret, "sret", "", "Steps forward until ret is called\n on the current execution" + " stack\n level."); + con_hook_command(c_resource_id, "resource_id", "i", "Identifies a resource number by\n" + " splitting it up in resource type\n and resource number."); + con_hook_command(c_clear_screen, "clear_screen", "", "Clears the screen, shows the\n background pic and picviews"); + con_hook_command(c_redraw_screen, "redraw_screen", "", "Redraws the screen"); + con_hook_command(c_debuglog, "debuglog", "!s*", "Sets the debug log modes.\n Possible parameters:\n" + " +x (sets debugging for x)\n -x (unsets debugging for x)\n\nPossible values" + " for x:\n u: Unimpl'd/stubbed stuff\n l: Lists and nodes\n g: Graphics\n" + " c: Character handling\n m: Memory management\n f: Function call checks\n" + " b: Bresenham details\n a: Audio\n d: System gfx management\n s: Base setter\n" + " p: Parser\n M: The menu system\n S: Said specs\n F: File I/O\n t: GetTime\n" + " e: 0.3.3 kernel emulation\n r: Room numbers\n P: Pathfinding\n" + " *: Everything\n\n" + " If invoked withour parameters,\n it will list all activated\n debug options.\n\n" + "SEE ALSO\n" + " gfx_debuglog.1, sfx_debuglog.1\n"); + con_hook_command(c_visible_map, "set_vismap", "i", "Sets the visible map.\n Default is 0 (visual).\n" + " Other useful values are:\n 1: Priority\n 2: Control\n 3: Auxiliary\n"); + con_hook_command(c_simkey, "simkey", "i", "Simulates a keypress with the\n specified scancode.\n"); + con_hook_command(c_statusbar, "statusbar", "ii", "Sets the colors of the status bar. Also controllable from the script.\n"); + con_hook_command(c_bpx, "bpx", "s", "Sets a breakpoint on the execution of\n the specified method.\n\n EXAMPLE:\n" + " bpx ego::doit\n\n May also be used to set a breakpoint\n that applies whenever an object\n" + " of a specific type is touched:\n bpx foo::\n"); + con_hook_command(c_bpe, "bpe", "ii", "Sets a breakpoint on the execution of specified" + " exported function.\n"); + con_hook_command(c_bplist, "bplist", "", "Lists all breakpoints.\n"); + con_hook_command(c_bpdel, "bpdel", "i", "Deletes a breakpoint with specified index."); + con_hook_command(c_go, "go", "", "Executes the script.\n"); + con_hook_command(c_dumpnodes, "dumpnodes", "i", "shows the specified number of nodes\n" + " from the parse node tree"); + con_hook_command(c_save_game, "save_game", "s", "Saves the current game state to\n the hard disk"); + con_hook_command(c_restore_game, "restore_game", "s", "Restores a saved game from the\n hard disk"); + con_hook_command(c_restart_game, "restart", "s*", "Restarts the game.\n\nUSAGE\n\n restart [-r] [-p]" + " [--play] [--replay]\n\n There are two ways to restart an SCI\n game:\n" + " play (-p) calls the game object's play()\n method\n replay (-r) calls the replay() method"); + con_hook_command(c_mousepos, "mousepos", "", + "Reveal the location of a mouse click.\n\n"); + con_hook_command(c_viewinfo, "viewinfo", "ii", "Displays the number of loops\n and cels of each loop" + " for the\n specified view resource and palette."); + con_hook_command(c_list_sentence_fragments, "list_sentence_fragments", "", "Lists all sentence fragments (which\n" + " are used to build Parse trees)."); + con_hook_command(c_parse, "parse", "s", "Parses a sequence of words and prints\n the resulting parse tree.\n" + " The word sequence must be provided as a\n single string."); + con_hook_command(c_gnf, "gnf", "", "Displays the Parse grammar\n in strict GNF"); + con_hook_command(c_set_parse_nodes, "set_parse_nodes", "s*", "Sets the contents of all parse nodes.\n" + " Input token must be separated by\n blanks."); + con_hook_command(c_sfx_debuglog, "sfx_debuglog", "s*", + "Sets or prints the sound subsystem debug\n" + "settings\n\n" + "USAGE\n\n" + " sfx_debuglog {[+|-][p|u|x|b]+}*\n\n" + " sfx_debuglog\n\n" + " Prints current settings\n\n" + " sfx_debuglog +X\n\n" + " Activates all debug features listed in X\n\n" + " sfx_debuglog -X\n\n" + " Deactivates the debug features listed in X\n\n" + " Debug features:\n" + " s: Active song changes\n" + " c: Song cues\n" + "SEE ALSO\n" + " debuglog.1, gfx_debuglog.1\n"); + con_hook_command(c_gfx_debuglog, "gfx_debuglog", "s*", + "Sets or prints the gfx driver's debug\n" + "settings\n\n" + "USAGE\n\n" + " gfx_debuglog {[+|-][p|u|x|b]+}*\n\n" + " gfx_debuglog\n\n" + " Prints current settings\n\n" + " gfx_debuglog +X\n\n" + " Activates all debug features listed in X\n\n" + " gfx_debuglog -X\n\n" + " Deactivates the debug features listed in X\n\n" + " Debug features:\n" + " p: Pointer\n" + " u: Updates\n" + " x: Pixmaps\n" + " b: Basic features\n\n" + "SEE ALSO\n" + " debuglog.1, sfx_debuglog.1\n"); + +#ifdef SCI_SIMPLE_SAID_CODE + con_hook_command(c_sim_parse, "simparse", "s*", "Simulates a parsed entity.\n\nUSAGE\n Call this" + " function with a list of\n Said operators, words, and word group" + "\n numbers to match Said() specs\n that look identical.\n" + "\n Note that opening braces and\n everything behind them are\n" + "\n removed from all non-operator\n parameter tokens.\n" + "\n simparse without parameters\n removes the entity.\n"); +#endif /* SCI_SIMPLE_SAID_CODE */ +#ifdef GFXW_DEBUG_WIDGETS + con_hook_command(c_gfx_print_widget, "gfx_print_widget", "i*", "If called with no parameters, it\n shows which widgets are active.\n" + " With parameters, it lists the\n widget corresponding to the\n numerical index specified (for\n each parameter)."); +#endif + con_hook_command(c_gfx_flush_resources, "gfx_free_widgets", "", "Frees all dynamically allocated\n widgets (for memory profiling).\n"); + con_hook_command(c_gfx_current_port, "gfx_current_port", "", "Determines the current port number"); + con_hook_command(c_gfx_print_port, "gfx_print_port", "i*", "Displays all information about the\n specified port," + " or the current port\n if no port was specified."); + con_hook_command(c_gfx_print_visual, "gfx_print_visual", "", "Displays all information about the\n current widget state"); + con_hook_command(c_gfx_print_dynviews, "gfx_print_dynviews", "", "Shows the dynview list"); + con_hook_command(c_gfx_print_dropviews, "gfx_print_dropviews", "", "Shows the list of dropped\n dynviews"); + con_hook_command(c_gfx_drawpic, "gfx_drawpic", "ii*", "Draws a pic resource\n\nUSAGE\n gfx_drawpic [ []]\n" + " where is the number of the pic resource\n to draw\n is the optional default\n palette for the pic (0 is" + "\n assumed if not specified)\n are any pic draw flags (default\n is 1)"); + con_hook_command(c_dump_words, "dumpwords", "", "Lists all parser words"); + con_hook_command(c_gfx_show_map, "gfx_show_map", "i", "Shows one of the screen maps\n Semantics of the int parameter:\n" + " 0: visual map (back buffer)\n 1: priority map (back buf.)\n 2: control map (static buf.)"); + con_hook_command(c_gfx_fill_screen, "gfx_fill_screen", "i", "Fills the screen with one\n of the EGA colors\n"); + con_hook_command(c_gfx_draw_rect, "gfx_draw_rect", "iiiii", "Draws a rectangle to the screen\n with one of the EGA colors\n\nUSAGE\n\n" + " gfx_draw_rect "); + con_hook_command(c_gfx_propagate_rect, + "gfx_propagate_rect", + "iiiii", + "Propagates a lower gfx buffer to a\n" + " higher gfx buffer.\n\nUSAGE\n\n" + " gfx_propagate_rect \n"); + con_hook_command(c_gfx_update_zone, "gfx_update_zone", "iiii", "Propagates a rectangular area from\n the back buffer to the front buffer" + "\n\nUSAGE\n\n" + " gfx_update_zone "); +#ifdef __GNUC__ +#warning "Re-enable con:draw_viewobj" +#endif +#if 0 + con_hook_command(c_gfx_draw_viewobj, "draw_viewobj", "i", "Draws the nsRect and brRect of a\n dynview object.\n\n nsRect is green, brRect\n" + " is blue.\n"); +#endif + con_hook_command(c_gfx_draw_cel, "gfx_draw_cel", "iiii", "Draws a single view\n cel to the center of the\n screen\n\n" + "USAGE\n gfx_draw_cel \n"); + con_hook_command(c_gfx_priority, "gfx_priority", "i*", "Prints information about priority\n bands\nUSAGE\n\n gfx_priority\n\n" + " will print the min and max values\n for the priority bands\n\n gfx_priority \n\n Print start of the priority\n" + " band for the specified\n priority\n"); + con_hook_command(c_segtable, "segtable", "!", + "Gives a short listing of all segments\n\n" + "SEE ALSO\n\n" + " seginfo.1"); + con_hook_command(c_segkill, "segkill", "!i*", + "Deletes the specified segment\n\n" + "USAGE\n\n" + " segkill \n"); + con_hook_command(c_seginfo, "seginfo", "!i*", + "Explains the specified segment\n\n" + "USAGE\n\n" + " seginfo\n" + " seginfo \n" + " Either explains all active segments\n" + " (no parameter) or the specified one.\n\n" + "SEE ALSO\n\n" + " segtable.1"); + con_hook_command(c_vo, "vo", "!a", + "Examines an object\n\n" + "SEE ALSO\n\n" + " addresses.3, type.1"); + con_hook_command(c_vr, "vr", "!aa*", + "Examines an arbitrary reference\n\n"); + con_hook_command(c_sg, "sg", "!i", + "Steps until the global variable with the\n" + "specified index is modified.\n\nSEE ALSO\n\n" + " s.1, snk.1, so.1, bpx.1"); + con_hook_command(c_songlib_print, "songlib_print", "", + ""); +#ifdef HAVE_SYSV_IPC + con_hook_command(c_codebug, "codebug", "!s", + "Starts codebugging mode\n\nUSAGE\n\n" + " codebug path/to/old/freesci\n\n" + " A different version of FreeSCI may be\n" + " spawned as a child process; all commands\n" + " send to this debugger will also be sent to\n" + " to the child process.\n\nSEE ALSO\n\n codebugging.3"); +#endif + con_hook_command(c_type, "type", "!a", + "Determines the type of a value\n\n" + "SEE ALSO\n\n addresses.3, vo.1"); + con_hook_command(c_shownode, "shownode", "!a", + "Prints information about a list node\n" + " or list base.\n\n"); + con_hook_command(c_is_sample, "is-sample", "i", + "Tests whether a given sound resource\n" + " is a PCM sample, and displays infor-\n" + " mation on it if it is.\n\n"); + con_hook_command(c_sfx_01_header, "sfx-01-header", "i", + "Dumps the header of an SCI01 song\n\n" + "SEE ALSO\n\n" + " sfx-01-track.1\n\n"); + con_hook_command(c_sfx_01_track, "sfx-01-track", "ii", + "Dumps a track from an SCI01 song\n\n" + "USAGE\n\n" + " sfx-01-track \n\n" + "SEE ALSO\n\n" + " sfx-01-header.1\n\n"); + con_hook_command(c_sleep, "sleep", "i", "Suspends everything for the\n" + " specified number of seconds"); + con_hook_command(c_gc_show_reachable, "gc-list-reachable", "!a", + "Prints all addresses directly reachable from\n" + " the memory object specified as parameter.\n\n" + "SEE ALSO\n\n" + " gc-list-freeable.1, gc-normalise.1, gc.1,\n" + " gc-all-reachable.1"); + con_hook_command(c_gc_show_freeable, "gc-list-freeable", "!a", + "Prints all addresses freeable in the segment\n" + " associated with the address (offset is ignored).\n\n" + "SEE ALSO\n\n" + " gc-list-freeable.1, gc-normalise.1, gc.1,\n" + " gc-all-reachable.1"); + con_hook_command(c_gc_normalise, "gc-normalise", "!a", + "Prints the \"normal\" address of a given address,\n" + " i.e. the address we would free in order to free\n" + " the object associated with the original address.\n\n" + "SEE ALSO\n\n" + " gc-list-freeable.1, gc-list-reachable.1, gc.1,\n" + " gc-all-reachable.1"); + con_hook_command(c_gc, "gc", "", + "Performs garbage collection.\n\n" + "SEE ALSO\n\n" + " gc-list-freeable.1, gc-list-reachable.1,\n" + " gc-all-reachable.1, gc-normalise.1"); + con_hook_command(c_gc_list_reachable, "gc-all-reachable", "", + "Lists all reachable objects, normalised.\n\n" + "SEE ALSO\n\n" + " gc-list-freeable.1, gc-list-reachable.1,\n" + " gc.1, gc-normalise.1"); + + + con_hook_int(&script_debug_flag, "script_debug_flag", "Set != 0 to enable debugger\n"); + con_hook_int(&script_checkloads_flag, "script_checkloads_flag", "Set != 0 to display information\n" + " when scripts are loaded or unloaded"); + con_hook_int(&script_abort_flag, "script_abort_flag", "Set != 0 to abort execution\n"); + con_hook_int(&script_step_counter, "script_step_counter", "# of executed SCI operations\n"); + con_hook_int(&sci_debug_flags, "debug_flags", "Debug flags:\n 0x0001: Log each command executed\n" + " 0x0002: Break on warnings\n \0x0004: Print VM warnings\n"); + con_hook_int(&_weak_validations, "weak_validations", "Set != 0 to turn some validation errors\n" + " into warnings\n"); + + con_hook_int(&script_gc_interval, "gc-interval", "Number of kernel calls in between gcs"); + con_hook_int(&debug_sleeptime_factor, "sleep-factor", "Factor to multiply with wait times\n Set to 0 to speed up games"); + + con_hook_page("codebugging", + "Co-debugging allows to run two (sufficiently\n" + " recent) versions of FreeSCI concurrently,\n" + " with one acting as a client of the other.\n" + " Co-debugging can be started by calling\n" + " 'codebug' (see codebug.1); note that the\n" + " argument passed to it must be a version of\n" + " FreeSCI that performs fflush(NULL) before\n" + " each read; only late 0.3.3-devel and later\n" + " have this property.\n\n" + " In co-debug mode, all commands are sent to\n" + " both programs, UNLESS one of the following\n" + " prefixes is used:\n\n" + " '.' : Only sends to the foreground version\n" + " ':' : Only sends to tbe background version\n\n" + " For example, when running 0.3.3 from within\n" + " 0.6.0, \".version\" would determine the version\n" + " as 0.6.0, and \"0.3.3\" would be returned for\n" + " \":version\". Both versions would be print\n" + " if only \"version\" was invoked, each result\n" + " coming from a different process.\n\n" + "COLORS\n\n" + " Whenever possible, the background process will\n" + " have its output marked by a non-default color\n" + " (usually red).\n\n" + "TROUBLESHOOTING\n\n" + " If the background version appears to be silent,\n" + " make sure it is calling fflush(NULL) before\n" + " reading input.\n\n" + "SEE ALSO\n\n" + " codebug.1"); + + } /* If commands were not hooked up */ + + } + + if (_debug_step_running) + _debug_step_running--; + + while (_debugstate_valid) { + int skipfirst = 0; + const char *commandstring; +#ifdef WANT_CONSOLE + char *input; +#endif + + /* Suspend music playing */ + sfx_suspend(&s->sound, 1); + +#ifdef WANT_CONSOLE +# ifndef FORCE_CONSOLE + if (!have_windowed) { +# endif + con_gfx_show(s->gfx_state); + input = con_gfx_read(s->gfx_state); + con_gfx_hide(s->gfx_state); + commandstring = input; + sciprintf("> %s\n", commandstring); +# ifndef FORCE_CONSOLE + } else +# endif +#endif +#ifndef FORCE_CONSOLE + commandstring = _debug_get_input(); + + /* Check if a specific destination has been given */ + if (commandstring + && (commandstring[0] == '.' + || commandstring[0] == ':')) + skipfirst = 1; + +#endif +#ifdef HAVE_SYSV_IPC + if (commandstring + && commandstring[0] != '.') { + codebug_send_command(commandstring + skipfirst); + codebug_send_command("\n"); + } +#endif + if (commandstring + && commandstring[0] != ':') + con_parse(s, commandstring + skipfirst); + sciprintf("\n"); + + /* Resume music playing */ + sfx_suspend(&s->sound, 0); + } +} + diff --git a/engines/sci/engine/seg_manager.c b/engines/sci/engine/seg_manager.c new file mode 100644 index 0000000000..8bc6187a22 --- /dev/null +++ b/engines/sci/engine/seg_manager.c @@ -0,0 +1,2063 @@ +/*************************************************************************** + seg_manager.c Copyright (C) 2002 Xiaojun Chen, Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include +#include +#include + + +/*#define GC_DEBUG*/ /* Debug garbage collection */ +/*#define GC_DEBUG_VERBOSE*/ /* Debug garbage verbosely */ + +#define SM_MEMORY_POISON /* Poison memory upon deallocation */ + +mem_obj_t* mem_obj_allocate(seg_manager_t *self, seg_id_t segid, int hash_id, mem_obj_enum type); + +#undef DEBUG_SEG_MANAGER /* Define to turn on debugging */ +#define GET_SEGID() if (flag == SCRIPT_ID) \ + id = sm_seg_get (self, id); \ + VERIFY ( sm_check (self, id), "invalid seg id" ); +#define VERIFY_MEM( mem_ptr, ret ) if (! (mem_ptr) ) {\ + sciprintf( "%s, *d, no enough memory", __FILE__, __LINE__ ); \ + return ret; \ + } + +#define INVALID_SCRIPT_ID -1 + +void dbg_print( const char* msg, void *i ) { +#ifdef DEBUG_SEG_MANAGER + char buf[1000]; + sprintf( buf, "%s = [0x%x], dec:[%d]", msg, i, i); + perror( buf ); +#endif +} + +/*--------------------------*/ +/*-- forward declarations --*/ +/*--------------------------*/ + +void +sm_script_initialise_locals_zero(seg_manager_t *self, seg_id_t seg, int count); + +void +sm_script_initialise_locals(seg_manager_t *self, reg_t location); + +static int +_sm_deallocate (seg_manager_t* self, int seg, int recursive); + +static hunk_t * +sm_alloc_hunk(seg_manager_t *self, reg_t *); + +static void +sm_free_hunk(seg_manager_t *self, reg_t addr); + +static int +sm_check(seg_manager_t* self, int seg); +/* Check segment validity +** Parameters: (int) seg: The segment to validate +** Returns : (int) 0 if 'seg' is an invalid segment +** 1 if 'seg' is a valid segment +*/ + +/*******************************/ +/** End of Memory Management **/ +/*******************************/ + +static inline int +find_free_id(seg_manager_t *self, int *id) +{ + char was_added = 0; + int retval; + + while (!was_added) { + retval = int_hash_map_check_value(self->id_seg_map, self->reserved_id, + 1, &was_added); + *id = self->reserved_id--; + if (self->reserved_id < -1000000) + self->reserved_id = -10; + /* Make sure we don't underflow */ + } + + return retval; +} + +static mem_obj_t * +alloc_nonscript_segment(seg_manager_t *self, mem_obj_enum type, seg_id_t *segid) +{ /* Allocates a non-script segment */ + int id; + *segid = find_free_id(self, &id); + return mem_obj_allocate(self, *segid, id, type); +} + + +void sm_init(seg_manager_t* self, int sci1_1) { + int i; + + self->mem_allocated = 0; /* Initialise memory count */ + + self->id_seg_map = new_int_hash_map(); + self->reserved_id = INVALID_SCRIPT_ID; + int_hash_map_check_value (self->id_seg_map, self->reserved_id, 1, NULL); /* reserve 0 for seg_id */ + self->reserved_id--; /* reserved_id runs in the reversed direction to make sure no one will use it. */ + + self->heap_size = DEFAULT_SCRIPTS; + self->heap = (mem_obj_t**) sci_calloc(self->heap_size, sizeof(mem_obj_t *)); + + self->clones_seg_id = 0; + self->lists_seg_id = 0; + self->nodes_seg_id = 0; + self->hunks_seg_id = 0; + + self->exports_wide = 0; + self->sci1_1 = sci1_1; + + /* initialize the heap pointers*/ + for (i = 0; i < self->heap_size; i++) { + self->heap[i] = NULL; + } + + /* gc initialisation */ + self->gc_mark_bits = 0; +} + +/* destroy the object, free the memorys if allocated before */ +void sm_destroy (seg_manager_t* self) { + int i; + /* free memory*/ + for (i = 0; i < self->heap_size; i++) { + if (self->heap[i]) + _sm_deallocate(self, i, 0); + } + + free_int_hash_map(self->id_seg_map); + + sci_free (self->heap); + self->heap = NULL; +} + +/* allocate a memory for script from heap +** Parameters: (state_t *) s: The state to operate on +** (int) script_nr: The script number to load +** Returns : 0 - allocation failure +** 1 - allocated successfully +** seg_id - allocated segment id +*/ +mem_obj_t* sm_allocate_script (seg_manager_t* self, struct _state *s, int script_nr, int* seg_id) { + int seg; + char was_added; + mem_obj_t* mem; + + seg = int_hash_map_check_value (self->id_seg_map, script_nr, 1, &was_added); + if (!was_added) { + *seg_id = seg; + return self->heap[*seg_id]; + } + + /* allocate the mem_obj_t */ + mem = mem_obj_allocate(self, seg, script_nr, MEM_OBJ_SCRIPT); + if (!mem) { + sciprintf("%s, %d, Not enough memory, ", __FILE__, __LINE__ ); + return NULL; + } + + *seg_id = seg; + return mem; +} + +static void sm_set_script_size(mem_obj_t *mem, struct _state *s, int script_nr) +{ + resource_t *script = scir_find_resource(s->resmgr, sci_script, script_nr, 0); + resource_t *heap = scir_find_resource(s->resmgr, sci_heap, script_nr, 0); + + mem->data.script.script_size = script->size; + mem->data.script.heap_size = 0; /* Set later */ + + if (!script || (s->version >= SCI_VERSION(1,001,000) && !heap)) + { + sciprintf("%s: failed to load %s\n", __FUNCTION__, + !script ? "script" : "heap"); + return; + } + if (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER) { + mem->data.script.buf_size = script->size + getUInt16(script->data)*2; + /* locals_size = getUInt16(script->data)*2; */ + } + else if (s->version < SCI_VERSION(1,001,000)) { + mem->data.script.buf_size = script->size; + } + else + { + mem->data.script.buf_size = script->size + heap->size; + mem->data.script.heap_size = heap->size; + + /* Ensure that the start of the heap resource can be word-aligned. */ + if (script->size & 2) + { + mem->data.script.buf_size++; + mem->data.script.script_size++; + } + + if (mem->data.script.buf_size > 65535) + { + sciprintf("Script and heap sizes combined exceed 64K.\n" + "This means a fundamental design bug was made in FreeSCI\n" + "regarding SCI1.1 games.\nPlease report this so it can be" + "fixed in the next major version!\n"); + return; + } + } +} + +int sm_initialise_script(mem_obj_t *mem, struct _state *s, int script_nr) +{ + /* allocate the script.buf */ + script_t *scr; + + sm_set_script_size(mem, s, script_nr); + mem->data.script.buf = (byte*) sci_malloc (mem->data.script.buf_size); + + dbg_print( "mem->data.script.buf ", mem->data.script.buf ); + if (!mem->data.script.buf) { + sm_free_script ( mem ); + sciprintf("seg_manager.c: Not enough memory space for script size" ); + mem->data.script.buf_size = 0; + return 0; + } + + /* Initialize objects */ + scr = &(mem->data.script); + scr->objects = NULL; + scr->objects_allocated = 0; + scr->objects_nr = 0; /* No objects recorded yet */ + + scr->locals_offset = 0; + scr->locals_block = NULL; + + scr->code = NULL; + scr->code_blocks_nr = 0; + scr->code_blocks_allocated = 0; + + scr->nr = script_nr; + scr->marked_as_deleted = 0; + scr->relocated = 0; + + scr->obj_indices = new_int_hash_map(); + + if (s->version >= SCI_VERSION(1,001,000)) + scr->heap_start = scr->buf + scr->script_size; + else + scr->heap_start = scr->buf; + + return 1; +} + +int +_sm_deallocate (seg_manager_t* self, int seg, int recursive) +{ + mem_obj_t *mobj; + VERIFY ( sm_check (self, seg), "invalid seg id" ); + + mobj = self->heap[seg]; + int_hash_map_remove_value( self->id_seg_map, mobj->segmgr_id ); + + switch (mobj->type) { + + case MEM_OBJ_SCRIPT: + sm_free_script ( mobj ); + + mobj->data.script.buf = NULL; + if (recursive && mobj->data.script.locals_segment) + _sm_deallocate(self, mobj->data.script.locals_segment, + recursive); + break; + + case MEM_OBJ_LOCALS: + sci_free(mobj->data.locals.locals); + mobj->data.locals.locals = NULL; + break; + + case MEM_OBJ_DYNMEM: + if (mobj->data.dynmem.buf) + sci_free(mobj->data.dynmem.buf); + mobj->data.dynmem.buf = NULL; + break; + case MEM_OBJ_SYS_STRINGS: + sys_string_free_all(&(mobj->data.sys_strings)); + break; + case MEM_OBJ_STACK: + sci_free(mobj->data.stack.entries); + mobj->data.stack.entries = NULL; + break; + case MEM_OBJ_LISTS: + sci_free(mobj->data.lists.table); + mobj->data.lists.table = NULL; + mobj->data.lists.entries_nr = mobj->data.lists.max_entry = 0; + break; + case MEM_OBJ_NODES: + sci_free(mobj->data.nodes.table); + mobj->data.nodes.table = NULL; + mobj->data.nodes.entries_nr = mobj->data.nodes.max_entry = 0; + break; + case MEM_OBJ_CLONES: + sci_free(mobj->data.clones.table); + mobj->data.clones.table = NULL; + mobj->data.clones.entries_nr = mobj->data.clones.max_entry = 0; + break; + case MEM_OBJ_HUNK: + sci_free(mobj->data.hunks.table); + mobj->data.hunks.table = NULL; + mobj->data.hunks.entries_nr = mobj->data.hunks.max_entry = 0; + break; + case MEM_OBJ_RESERVED: + sci_free(mobj->data.reserved); + break; + default: + fprintf(stderr, "Deallocating segment type %d not supported!\n", + mobj->type); + BREAKPOINT(); + } + + free(mobj); + self->heap[seg] = NULL; + + return 1; +} + +int sm_script_marked_deleted(seg_manager_t* self, int script_nr) +{ + script_t *scr; + int seg = sm_seg_get( self, script_nr ); + VERIFY ( sm_check (self, seg), "invalid seg id" ); + + scr = &(self->heap[seg]->data.script); + return scr->marked_as_deleted; +} + +void sm_mark_script_deleted(seg_manager_t* self, int script_nr) +{ + script_t *scr; + int seg = sm_seg_get( self, script_nr ); + VERIFY ( sm_check (self, seg), "invalid seg id" ); + + scr = &(self->heap[seg]->data.script); + scr->marked_as_deleted = 1; +} + +void sm_unmark_script_deleted(seg_manager_t* self, int script_nr) +{ + script_t *scr; + int seg = sm_seg_get( self, script_nr ); + VERIFY ( sm_check (self, seg), "invalid seg id" ); + + scr = &(self->heap[seg]->data.script); + scr->marked_as_deleted = 0; +} + +int +sm_script_is_marked_as_deleted(seg_manager_t* self, seg_id_t seg) +{ + script_t *scr; + + if (!sm_check (self, seg)) + return 0; + + if (self->heap[seg]->type != MEM_OBJ_SCRIPT) + return 0; + + scr = &(self->heap[seg]->data.script); + return scr->marked_as_deleted; +} + + +int sm_deallocate_script (seg_manager_t* self, int script_nr) { + int seg = sm_seg_get( self, script_nr ); + + _sm_deallocate(self, seg, 1); + return 1; +} + +mem_obj_t* +mem_obj_allocate(seg_manager_t *self, seg_id_t segid, int hash_id, mem_obj_enum type) +{ + mem_obj_t* mem = ( mem_obj_t* ) sci_calloc( sizeof (mem_obj_t), 1 ); + if( !mem ) { + sciprintf( "seg_manager.c: invalid mem_obj " ); + return NULL; + } + + if (segid >= self->heap_size) { + void *temp; + int oldhs = self->heap_size; + + if (segid >= self->heap_size * 2) { + sciprintf( "seg_manager.c: hash_map error or others??" ); + return NULL; + } + self->heap_size *= 2; + temp = sci_realloc ((void*)self->heap, self->heap_size * sizeof( mem_obj_t* ) ); + if (!temp) { + sciprintf("seg_manager.c: Not enough memory space for script size" ); + return NULL; + } + self->heap = (mem_obj_t**) temp; + + /* Clear pointers */ + memset(self->heap + oldhs, 0, sizeof(mem_obj_t *) * (self->heap_size - oldhs)); + } + + mem->segmgr_id = hash_id; + mem->type = type; + + /* hook it to the heap */ + self->heap[segid] = mem; + return mem; +} + +/* No longer in use? */ +/* void sm_object_init (object_t* object) { */ +/* if( !object ) return; */ +/* object->variables_nr = 0; */ +/* object->variables = NULL; */ +/* }; */ + +void +sm_free_script ( mem_obj_t* mem ) +{ + if( !mem ) return; + if( mem->data.script.buf ) { + sci_free( mem->data.script.buf ); + mem->data.script.buf = NULL; + mem->data.script.buf_size = 0; + } + if( mem->data.script.objects ) { + int i; + for( i = 0; i < mem->data.script.objects_nr; i++ ) { + object_t* object = &mem->data.script.objects[i]; + if( object->variables ) { + free( object->variables ); + object->variables = NULL; + object->variables_nr = 0; + } + } + free( mem->data.script.objects ); + mem->data.script.objects = NULL; + mem->data.script.objects_nr = 0; + } + + free_int_hash_map(mem->data.script.obj_indices); + if (NULL != mem->data.script.code) { + sci_free(mem->data.script.code); + } +} + +/* memory operations */ +static void +sm_mset (seg_manager_t* self, int offset, int c, size_t n, int id, int flag) { + mem_obj_t* mem_obj; + GET_SEGID(); + mem_obj = self->heap[id]; + switch (mem_obj->type) { + case MEM_OBJ_SCRIPT: + if (mem_obj->data.script.buf) { + memset( mem_obj->data.script.buf + offset, c, n ); + } + break; + case MEM_OBJ_CLONES: + sciprintf( "memset for clones haven't been implemented\n" ); + break; + default: + sciprintf( "unknown mem obj type\n" ); + break; + } +} + +static void +sm_mcpy_in_in (seg_manager_t* self, int dst, const int src, size_t n, int id, int flag) { + mem_obj_t* mem_obj; + GET_SEGID(); + mem_obj = self->heap[id]; + switch (mem_obj->type) { + case MEM_OBJ_SCRIPT: + if (mem_obj->data.script.buf) { + memcpy ( mem_obj->data.script.buf + dst, mem_obj->data.script.buf + src, n ); + } + break; + case MEM_OBJ_CLONES: + sciprintf( "memcpy for clones haven't been implemented\n" ); + break; + default: + sciprintf( "unknown mem obj type\n" ); + break; + } +} + +void +sm_mcpy_in_out (seg_manager_t* self, int dst, const void* src, size_t n, int id, int flag) { + mem_obj_t* mem_obj; + GET_SEGID(); + mem_obj = self->heap[id]; + switch (mem_obj->type) { + case MEM_OBJ_SCRIPT: + if (mem_obj->data.script.buf) { + memcpy ( mem_obj->data.script.buf + dst, src, n ); + } + break; + case MEM_OBJ_CLONES: + sciprintf( "memcpy for clones hasn't been implemented yet\n" ); + break; + default: + sciprintf( "unknown mem obj type\n" ); + break; + } +} + +static void +sm_mcpy_out_in (seg_manager_t* self, void* dst, const int src, size_t n, int id, int flag) { + mem_obj_t* mem_obj; + GET_SEGID(); + mem_obj = self->heap[id]; + switch (mem_obj->type) { + case MEM_OBJ_SCRIPT: + if (mem_obj->data.script.buf) { + memcpy ( dst, mem_obj->data.script.buf + src, n ); + } + break; + case MEM_OBJ_CLONES: + sciprintf( "memcpy for clones hasn't been implemented yet\n" ); + break; + default: + sciprintf( "unknown mem obj type\n" ); + break; + } +} + +gint16 +sm_get_heap (seg_manager_t* self, reg_t reg) +{ + mem_obj_t* mem_obj; + mem_obj_enum mem_type; + + VERIFY( sm_check (self, reg.segment), "Invalid seg id" ); + mem_obj = self->heap[reg.segment]; + mem_type = mem_obj->type; + + switch( mem_type ) { + case MEM_OBJ_SCRIPT: + VERIFY( reg.offset + 1 < mem_obj->data.script.buf_size, "invalid offset\n" ); + return (unsigned char)mem_obj->data.script.buf[reg.offset] | + ( ((unsigned char)mem_obj->data.script.buf[reg.offset+1]) << 8 ); + case MEM_OBJ_CLONES: + sciprintf( "memcpy for clones hasn't been implemented yet\n" ); + break; + default: + sciprintf( "unknown mem obj type\n" ); + break; + } + return 0; /* never get here */ +} + +void sm_put_heap (seg_manager_t* self, reg_t reg, gint16 value ) { + mem_obj_t* mem_obj; + mem_obj_enum mem_type; + + VERIFY( sm_check (self, reg.segment), "Invalid seg id" ); + mem_obj = self->heap[reg.segment]; + mem_type = mem_obj->type; + + switch( mem_type ) { + case MEM_OBJ_SCRIPT: + VERIFY( reg.offset + 1 < mem_obj->data.script.buf_size, "invalid offset" ); + mem_obj->data.script.buf[reg.offset] = value & 0xff; + mem_obj->data.script.buf[reg.offset + 1] = value >> 8; + break; + case MEM_OBJ_CLONES: + sciprintf( "memcpy for clones haven't been implemented\n" ); + break; + default: + sciprintf( "unknown mem obj type\n" ); + break; + } +} + +/* return the seg if script_id is valid and in the map, else -1 */ +int sm_seg_get (seg_manager_t* self, int script_id) +{ + return int_hash_map_check_value (self->id_seg_map, script_id, 0, NULL); +} + +/* validate the seg +** return: +** 0 - invalid seg +** 1 - valid seg +*/ +static int +sm_check (seg_manager_t* self, int seg) { + if ( seg < 0 || seg >= self->heap_size ) { + return 0; + } + if (!self->heap[seg]) { + sciprintf("seg_manager.c: seg %x is removed from memory, but not removed from hash_map\n", seg ); + return 0; + } + return 1; +} + +int sm_script_is_loaded (seg_manager_t* self, int id, id_flag flag) { + if (flag == SCRIPT_ID) + id = sm_seg_get (self, id); + return sm_check (self, id); +} + +void sm_increment_lockers (seg_manager_t* self, int id, id_flag flag) { + if (flag == SCRIPT_ID) + id = sm_seg_get (self, id); + VERIFY ( sm_check (self, id), "invalid seg id" ); + self->heap[id]->data.script.lockers++; +} + +void sm_decrement_lockers (seg_manager_t* self, int id, id_flag flag) { + if (flag == SCRIPT_ID) + id = sm_seg_get (self, id); + VERIFY ( sm_check (self, id), "invalid seg id" ); + + if (self->heap[id]->data.script.lockers > 0) + self->heap[id]->data.script.lockers--; +} + +int sm_get_lockers (seg_manager_t* self, int id, id_flag flag) { + if (flag == SCRIPT_ID) + id = sm_seg_get (self, id); + VERIFY ( sm_check (self, id), "invalid seg id" ); + return self->heap[id]->data.script.lockers; +} + +void sm_set_lockers (seg_manager_t* self, int lockers, int id, id_flag flag) { + if (flag == SCRIPT_ID) + id = sm_seg_get (self, id); + VERIFY ( sm_check (self, id), "invalid seg id" ); + self->heap[id]->data.script.lockers = lockers; +} + +void +sm_set_export_table_offset (struct _seg_manager_t* self, int offset, int id, id_flag flag) +{ + script_t *scr = &(self->heap[id]->data.script); + + GET_SEGID(); + if (offset) { + scr->export_table = (guint16 *)(scr->buf + offset + 2); + scr->exports_nr = getUInt16((byte *)(scr->export_table - 1)); + } else { + scr->export_table = NULL; + scr->exports_nr = 0; + } +} + +int +sm_hash_segment_data(struct _seg_manager_t* self, int id) +{ + int i, len, hash_code = 0x55555555; + char *buf; + + if (self->heap[id]->type == MEM_OBJ_LISTS) return 0; + if (self->heap[id]->type == MEM_OBJ_NODES) return 0; + if (self->heap[id]->type == MEM_OBJ_CLONES) return 0; + buf = (char*)sm_dereference(self, make_reg(id, 0), &len); + + for (i = 0; i < len; i++) + hash_code = (hash_code * 19) + *(buf + i); + + return hash_code; +} + +void +sm_set_export_width(struct _seg_manager_t* self, int flag) +{ + self->exports_wide = flag; +} + +static guint16 * +sm_get_export_table_offset (struct _seg_manager_t* self, int id, int flag, int *max) +{ + GET_SEGID(); + if (max) + *max = self->heap[id]->data.script.exports_nr; + return self->heap[id]->data.script.export_table; +} + +void +sm_set_synonyms_offset (struct _seg_manager_t* self, int offset, int id, id_flag flag) { + GET_SEGID(); + self->heap[id]->data.script.synonyms = + self->heap[id]->data.script.buf + offset; +} + +byte * +sm_get_synonyms(seg_manager_t *self, int id, id_flag flag) +{ + GET_SEGID(); + return self->heap[id]->data.script.synonyms; +} + +void +sm_set_synonyms_nr (struct _seg_manager_t* self, int nr, int id, id_flag flag) { + GET_SEGID(); + self->heap[id]->data.script.synonyms_nr = nr; +} + +int +sm_get_synonyms_nr (struct _seg_manager_t* self, int id, id_flag flag) +{ + GET_SEGID(); + return self->heap[id]->data.script.synonyms_nr; +} + +static int +sm_get_heappos (struct _seg_manager_t* self, int id, int flag) +{ + GET_SEGID(); + return 0; +} + +static void +sm_set_variables (struct _seg_manager_t* self, reg_t reg, int obj_index, reg_t variable_reg, int variable_index ) { + script_t* script; + VERIFY ( sm_check (self, reg.segment), "invalid seg id" ); + VERIFY ( self->heap[reg.segment], "invalid mem" ); + + script = &(self->heap[reg.segment]->data.script); + + VERIFY( obj_index < script->objects_nr, "Invalid obj_index" ); + + VERIFY( variable_index >= 0 + && variable_index < script->objects[obj_index].variables_nr, + "Attempt to write to invalid variable number" ); + + script->objects[obj_index].variables[variable_index] = variable_reg; +} + + +static inline int +_relocate_block(seg_manager_t *self, reg_t *block, int block_location, int block_items, seg_id_t segment, int location) +{ + int rel = location - block_location; + int index; + + if (rel < 0) + return 0; + + index = rel >> 1; + + if (index >= block_items) + return 0; + + if (rel & 1) { + sciprintf("Error: Attempt to relocate odd variable #%d.5e (relative to %04x)\n", + index, block_location); + return 0; + } + block[index].segment = segment; /* Perform relocation */ + if (self->sci1_1) + block[index].offset += self->heap[segment]->data.script.script_size; + + return 1; +} + +static inline int +_relocate_local(seg_manager_t *self, script_t *scr, seg_id_t segment, int location) +{ + if (scr->locals_block) + return _relocate_block(self, + scr->locals_block->locals, scr->locals_offset, + scr->locals_block->nr, + segment, location); + else + return 0; /* No hands, no cookies */ +} + +static inline int +_relocate_object(seg_manager_t *self, object_t *obj, seg_id_t segment, int location) +{ + return _relocate_block(self, + obj->variables, obj->pos.offset, obj->variables_nr, + segment, location); +} + +void +sm_script_add_code_block(seg_manager_t *self, reg_t location) +{ + mem_obj_t *mobj = self->heap[location.segment]; + script_t *scr; + int index; + + VERIFY( !(location.segment >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT), + "Attempt to add a code block to non-script\n" ); + + scr = &(mobj->data.script); + + if (++scr->code_blocks_nr > scr->code_blocks_allocated) + { + scr->code_blocks_allocated += DEFAULT_OBJECTS_INCREMENT; + scr->code = (code_block_t*)sci_realloc(scr->code, scr->code_blocks_allocated * + sizeof(code_block_t)); + } + + index = scr->code_blocks_nr - 1; + scr->code[index].pos = location; + scr->code[index].size = getUInt16(scr->buf + location.offset - 2); +} + +void +sm_script_relocate(seg_manager_t *self, reg_t block) +{ + mem_obj_t *mobj = self->heap[block.segment]; + script_t *scr; + int count; + int i; + + VERIFY( !(block.segment >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT), + "Attempt relocate non-script\n" ); + + scr = &(mobj->data.script); + + VERIFY( block.offset < scr->buf_size + && getUInt16(scr->buf + block.offset)*2 + block.offset < scr->buf_size, + "Relocation block outside of script\n" ); + + count = getUInt16(scr->buf + block.offset); + + for (i = 0; i <= count; i++) { + int pos = getUInt16(scr->buf + block.offset + 2 + (i*2)); + if (!pos) continue; /* FIXME: A hack pending investigation */ + + if (!_relocate_local(self, scr, block.segment, pos)) { + int k, done = 0; + + for (k = 0; !done && k < scr->objects_nr; k++) { + if (_relocate_object(self, scr->objects + k, block.segment, pos)) + done = 1; + } + + for (k = 0; !done && k < scr->code_blocks_nr; k++) { + if (pos >= scr->code[k].pos.offset && + pos < scr->code[k].pos.offset+scr->code[k].size) + done = 1; + } + + if (!done) { + sciprintf("While processing relocation block "PREG":\n", + PRINT_REG(block)); + sciprintf("Relocation failed for index %04x (%d/%d)\n", pos, i+1, count); + if (scr->locals_block) + sciprintf("- locals: %d at %04x\n", + scr->locals_block->nr, + scr->locals_offset); + else + sciprintf("- No locals\n"); + for (k = 0; k < scr->objects_nr; k++) + sciprintf("- obj#%d at %04x w/ %d vars\n", + k, + scr->objects[k].pos.offset, + scr->objects[k].variables_nr); +// SQ3 script 71 has broken relocation entries. +// Since this is mainstream, we can't break out as we used to do. + sciprintf("Trying to continue anyway...\n"); +// BREAKPOINT(); + } + } + } +} + +void +sm_heap_relocate(seg_manager_t *self, state_t *s, reg_t block) +{ + mem_obj_t *mobj = self->heap[block.segment]; + script_t *scr; + int count; + int i; + + VERIFY( !(block.segment >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT), + "Attempt relocate non-script\n" ); + + scr = &(mobj->data.script); + + VERIFY( block.offset < scr->heap_size + && getUInt16(scr->heap_start + block.offset)*2 + block.offset < scr->buf_size, + "Relocation block outside of script\n" ); + + if (scr->relocated) return; + scr->relocated = 1; + count = getUInt16(scr->heap_start + block.offset); + + for (i = 0; i < count; i++) { + int pos = getUInt16(scr->heap_start + block.offset + 2 + (i*2)) + scr->script_size; + + if (!_relocate_local(self, scr, block.segment, pos)) { + int k, done = 0; + + for (k = 0; !done && k < scr->objects_nr; k++) { + if (_relocate_object(self, scr->objects + k, block.segment, pos)) + done = 1; + } + + if (!done) { + sciprintf("While processing relocation block "PREG":\n", + PRINT_REG(block)); + sciprintf("Relocation failed for index %04x (%d/%d)\n", pos, i+1, count); + if (scr->locals_block) + sciprintf("- locals: %d at %04x\n", + scr->locals_block->nr, + scr->locals_offset); + else + sciprintf("- No locals\n"); + for (k = 0; k < scr->objects_nr; k++) + sciprintf("- obj#%d at %04x w/ %d vars\n", + k, + scr->objects[k].pos.offset, + scr->objects[k].variables_nr); + sciprintf("Triggering breakpoint...\n"); + BREAKPOINT(); + } + } + } +} + +#define INST_LOOKUP_CLASS(id) ((id == 0xffff)? NULL_REG : get_class_address(s, id, SCRIPT_GET_LOCK, NULL_REG)) + +reg_t +get_class_address(state_t *s, int classnr, int lock, reg_t caller); + +static object_t * +sm_script_obj_init0(seg_manager_t *self, state_t *s, reg_t obj_pos) +{ + mem_obj_t *mobj = self->heap[obj_pos.segment]; + script_t *scr; + object_t *obj; + int id; + int base = obj_pos.offset - SCRIPT_OBJECT_MAGIC_OFFSET; + reg_t temp; + + VERIFY( !(obj_pos.segment >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT), + "Attempt to initialize object in non-script\n" ); + + scr = &(mobj->data.script); + + VERIFY( base < scr->buf_size, + "Attempt to initialize object beyond end of script\n" ); + + if (!scr->objects) { + scr->objects_allocated = DEFAULT_OBJECTS; + scr->objects = (object_t*)sci_malloc(sizeof(object_t) * scr->objects_allocated); + } + if (scr->objects_nr == scr->objects_allocated) { + scr->objects_allocated += DEFAULT_OBJECTS_INCREMENT; + scr->objects = (object_t*)sci_realloc(scr->objects, + sizeof(object_t) + * scr->objects_allocated); + + } + + temp = make_reg(obj_pos.segment, base); + id = int_hash_map_check_value(scr->obj_indices, base, 1, NULL); + scr->objects_nr++; + + obj = scr->objects + id; + + VERIFY( base + SCRIPT_FUNCTAREAPTR_OFFSET < scr->buf_size, + "Function area pointer stored beyond end of script\n" ); + + { + byte *data = (byte *) (scr->buf + base); + int funct_area = getUInt16( data + SCRIPT_FUNCTAREAPTR_OFFSET ); + int variables_nr; + int functions_nr; + int is_class; + int i; + + obj->flags = 0; + obj->pos = temp; + + VERIFY ( base + funct_area < scr->buf_size, + "Function area pointer references beyond end of script" ); + + variables_nr = getUInt16( data + SCRIPT_SELECTORCTR_OFFSET ); + functions_nr = getUInt16( data + funct_area - 2 ); + is_class = getUInt16( data + SCRIPT_INFO_OFFSET ) & SCRIPT_INFO_CLASS; + + VERIFY ( base + funct_area + functions_nr * 2 + /* add again for classes, since those also store selectors */ + + (is_class? functions_nr * 2 : 0) < scr->buf_size, + "Function area extends beyond end of script" ); + + obj->variables_nr = variables_nr; + obj->variables = (reg_t*)sci_malloc(sizeof(reg_t) * variables_nr); + + obj->methods_nr = functions_nr; + obj->base = scr->buf; + obj->base_obj = data; + obj->base_method = (guint16 *) (data + funct_area); + obj->base_vars = NULL; + + for (i = 0; i < variables_nr; i++) + obj->variables[i] = make_reg(0, getUInt16(data + (i*2))); + } + + return obj; +} + +static object_t * +sm_script_obj_init11(seg_manager_t *self, state_t *s, reg_t obj_pos) +{ + mem_obj_t *mobj = self->heap[obj_pos.segment]; + script_t *scr; + object_t *obj; + int id; + int base = obj_pos.offset; + + VERIFY( !(obj_pos.segment >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT), + "Attempt to initialize object in non-script\n" ); + + scr = &(mobj->data.script); + + VERIFY( base < scr->buf_size, + "Attempt to initialize object beyond end of script\n" ); + + if (!scr->objects) { + scr->objects_allocated = DEFAULT_OBJECTS; + scr->objects = (object_t*)sci_malloc(sizeof(object_t) * scr->objects_allocated); + } + if (scr->objects_nr == scr->objects_allocated) { + scr->objects_allocated += DEFAULT_OBJECTS_INCREMENT; + scr->objects = (object_t*)sci_realloc(scr->objects, + sizeof(object_t) + * scr->objects_allocated); + + } + + id = int_hash_map_check_value(scr->obj_indices, obj_pos.offset, 1, NULL); + scr->objects_nr++; + + obj = scr->objects + id; + + VERIFY( base + SCRIPT_FUNCTAREAPTR_OFFSET < scr->buf_size, + "Function area pointer stored beyond end of script\n" ); + + { + byte *data = (byte *) (scr->buf + base); + guint16 *funct_area = (guint16 *) (scr->buf + getUInt16( data + 6 )); + guint16 *prop_area = (guint16 *) (scr->buf + getUInt16( data + 4 )); + int variables_nr; + int functions_nr; + int is_class; + int i; + + obj->flags = 0; + obj->pos = obj_pos; + + VERIFY ( (byte *) funct_area < scr->buf + scr->buf_size, + "Function area pointer references beyond end of script" ); + + variables_nr = getUInt16(data + 2); + functions_nr = *funct_area; + is_class = getUInt16( data + 14 ) & SCRIPT_INFO_CLASS; + + obj->base_method = funct_area; + obj->base_vars = prop_area; + + VERIFY ( ((byte *) funct_area + functions_nr) < scr->buf + scr->buf_size, + "Function area extends beyond end of script" ); + + obj->variables_nr = variables_nr; + obj->variable_names_nr = variables_nr; + obj->variables = (reg_t*)sci_malloc(sizeof(reg_t) * variables_nr); + + obj->methods_nr = functions_nr; + obj->base = scr->buf; + obj->base_obj = data; + + for (i = 0; i < variables_nr; i++) + obj->variables[i] = make_reg(0, getUInt16(data + (i*2))); + } + + return obj; +} + +object_t * +sm_script_obj_init(seg_manager_t *self, state_t *s, reg_t obj_pos) +{ + if (!self->sci1_1) + return sm_script_obj_init0(self, s, obj_pos); + else + return sm_script_obj_init11(self, s, obj_pos); +} + +static local_variables_t * +_sm_alloc_locals_segment(seg_manager_t *self, script_t *scr, int count) +{ + if (!count) { /* No locals */ + scr->locals_segment = 0; + scr->locals_block = NULL; + return NULL; + } else { + mem_obj_t *mobj; + local_variables_t *locals; + + if (scr->locals_segment) { + mobj = self->heap[scr->locals_segment]; + VERIFY(mobj != NULL, "Re-used locals segment was NULL'd out"); + VERIFY(mobj->type == MEM_OBJ_LOCALS, "Re-used locals segment did not consist of local variables"); + VERIFY(mobj->data.locals.script_id == scr->nr, "Re-used locals segment belonged to other script"); + } else + mobj = alloc_nonscript_segment(self, MEM_OBJ_LOCALS, + &scr->locals_segment); + + locals = scr->locals_block = &(mobj->data.locals); + locals->script_id = scr->nr; + locals->locals = (reg_t*)sci_calloc(count, sizeof(reg_t)); + locals->nr = count; + + return locals; + } +} + +void +sm_script_initialise_locals_zero(seg_manager_t *self, seg_id_t seg, int count) +{ + mem_obj_t *mobj = self->heap[seg]; + script_t *scr; + + VERIFY( !(seg >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT), + "Attempt to initialize locals in non-script\n" ); + + scr = &(mobj->data.script); + + scr->locals_offset = -count * 2; /* Make sure it's invalid */ + + _sm_alloc_locals_segment(self, scr, count); +} + +void +sm_script_initialise_locals(seg_manager_t *self, reg_t location) +{ + mem_obj_t *mobj = self->heap[location.segment]; + int count; + script_t *scr; + local_variables_t *locals; + + VERIFY( !(location.segment >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT), + "Attempt to initialize locals in non-script\n" ); + + scr = &(mobj->data.script); + + VERIFY( location.offset + 1 < scr->buf_size, + "Locals beyond end of script\n" ); + + if (self->sci1_1) + count = getUInt16(scr->buf + location.offset - 2); + else + count = (getUInt16(scr->buf + location.offset - 2) - 4) >> 1; + /* half block size */ + + scr->locals_offset = location.offset; + + if (!(location.offset + count * 2 + 1 < scr->buf_size)) { + sciprintf("Locals extend beyond end of script: offset %04x, count %x vs size %x\n", + location.offset, count, scr->buf_size); + count = (scr->buf_size - location.offset) >> 1; + } + + locals = _sm_alloc_locals_segment(self, scr, count); + if (locals) { + int i; + byte *base = (byte *) (scr->buf + location.offset); + + for (i = 0; i < count; i++) + locals->locals[i].offset = getUInt16(base + i*2); + } +} + +void +sm_script_relocate_exports_sci11(seg_manager_t *self, int seg) +{ + mem_obj_t *mobj = self->heap[seg]; + script_t *scr; + int i; + int location; + + VERIFY( !(seg >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT), + "Attempt to relocate exports in non-script\n" ); + + scr = &(mobj->data.script); + for (i = 0; i < scr->exports_nr; i++) + { + /* We are forced to use an ugly heuristic here to distinguish function + exports from object/class exports. The former kind points into the + script resource, the latter into the heap resource. */ + location = getUInt16((byte *)(scr->export_table + i)); + if (getUInt16(scr->heap_start + location) == SCRIPT_OBJECT_MAGIC_NUMBER) + { + putInt16((byte *)(scr->export_table + i), location+scr->heap_start-scr->buf); + } else + { + /* Otherwise it's probably a function export, + and we don't need to do anything. */ + } + } + +} + +void +sm_script_initialise_objects_sci11(seg_manager_t *self, state_t *s, int seg) +{ + mem_obj_t *mobj = self->heap[seg]; + script_t *scr; + byte *seeker; + + VERIFY( !(seg >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT), + "Attempt to relocate exports in non-script\n" ); + + scr = &(mobj->data.script); + seeker = scr->heap_start + 4 + getUInt16(scr->heap_start + 2) * 2; + + while (getUInt16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) + { + + if (getUInt16(seeker + 14) & SCRIPT_INFO_CLASS) + { + int classpos = seeker-scr->buf; + int species = getUInt16(seeker + 10); + + if (species < 0 || species >= s->classtable_size) { + sciprintf("Invalid species %d(0x%x) not in interval " + "[0,%d) while instantiating script %d\n", + species, species, s->classtable_size, + scr->nr); + script_debug_flag = script_error_flag = 1; + return; + } + + s->classtable[species].script = scr->nr; + s->classtable[species].reg.segment = seg; + s->classtable[species].reg.offset = classpos; + } + seeker += getUInt16(seeker + 2) * 2; + } + + seeker = scr->heap_start + 4 + getUInt16(scr->heap_start + 2) * 2; + while (getUInt16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) + { + reg_t reg; + object_t *obj; + + reg.segment = seg; + reg.offset = seeker-scr->buf; + obj = sm_script_obj_init(&s->seg_manager, s, reg); + +#if 0 + if (obj->variables[5].offset != 0xffff) + { + obj->variables[5] = + INST_LOOKUP_CLASS(obj->variables[5].offset); + + + base_obj = obj_get(s, obj->variables[5]); + obj->variable_names_nr = base_obj->variables_nr; + obj->base_obj = base_obj->base_obj; + } +#endif + + /* Copy base from species class, as we need its selector IDs */ + obj->variables[6] = + INST_LOOKUP_CLASS(obj->variables[6].offset); + + seeker += getUInt16(seeker + 2) * 2; + } + +} +void +sm_script_free_unused_objects(seg_manager_t *self, seg_id_t seg) +{ + mem_obj_t *mobj = self->heap[seg]; + script_t *scr; + + VERIFY( !(seg >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT), + "Attempt to free unused objects in non-script\n" ); + + + scr = &(mobj->data.script); + if (scr->objects_allocated > scr->objects_nr) { + if (scr->objects_nr) + scr->objects = (object_t*)sci_realloc(scr->objects, sizeof(object_t) + * scr->objects_nr); + else { + if (scr->objects_allocated) + sci_free(scr->objects); + scr->objects = NULL; + } + scr->objects_allocated = scr->objects_nr; + } +} + +static inline char *dynprintf(char *msg, ...) +{ + va_list argp; + char *buf = (char*)sci_malloc(strlen(msg) + 100); + + va_start(argp, msg); + vsprintf(buf, msg, argp); + va_end(argp); + + return buf; +} + + +dstack_t * +sm_allocate_stack(seg_manager_t *self, int size, seg_id_t *segid) +{ + mem_obj_t *memobj = alloc_nonscript_segment(self, MEM_OBJ_STACK, segid); + dstack_t *retval = &(memobj->data.stack); + + retval->entries = (reg_t*)sci_calloc(size, sizeof(reg_t)); + retval->nr = size; + + return retval; +} + +sys_strings_t * +sm_allocate_sys_strings(seg_manager_t *self, seg_id_t *segid) +{ + mem_obj_t *memobj = alloc_nonscript_segment(self, MEM_OBJ_SYS_STRINGS, segid); + sys_strings_t *retval = &(memobj->data.sys_strings); + + memset(retval, 0, sizeof(sys_string_t)*SYS_STRINGS_MAX); + + return retval; +} + +seg_id_t +sm_allocate_reserved_segment(seg_manager_t *self, char *src_name) +{ + seg_id_t segid; + mem_obj_t *memobj = alloc_nonscript_segment(self, MEM_OBJ_RESERVED, &segid); + char *name = sci_strdup(src_name); + + memobj->data.reserved = name; + + return segid; +} + +guint16 +sm_validate_export_func(struct _seg_manager_t* self, int pubfunct, int seg ) { + script_t* script; + guint16 offset; + VERIFY ( sm_check (self, seg), "invalid seg id" ); + VERIFY (self->heap[seg]->type == MEM_OBJ_SCRIPT, "Can only validate exports on scripts"); + + script = &self->heap[seg]->data.script; + if( script->exports_nr <= pubfunct ) { + sciprintf( "pubfunct is invalid" ); + return 0; + } + + if (self->exports_wide) pubfunct *= 2; + offset = getUInt16( (byte*)(script->export_table + pubfunct) ); + VERIFY ( offset < script->buf_size, "invalid export function pointer" ); + + return offset; +} + + +void +sm_free_hunk_entry(seg_manager_t *self, reg_t addr) +{ + sm_free_hunk(self, addr); +} + + +hunk_t * +sm_alloc_hunk_entry(seg_manager_t *self, const char *hunk_type, int size, reg_t *reg) +{ + hunk_t *h = sm_alloc_hunk(self, reg); + + if (!h) + return NULL; + + h->mem = sci_malloc(size); + h->size = size; + h->type = hunk_type; + + return h; +} + +static void +_clone_cleanup(clone_t *clone) +{ + if (clone->variables) + sci_free(clone->variables); /* Free the dynamically allocated memory part */ +} + +static void +_hunk_cleanup(hunk_t *hunk) +{ + if (hunk->mem) + free (hunk->mem); +} + +DEFINE_HEAPENTRY(list, 8, 4) +DEFINE_HEAPENTRY(node, 32, 16) +DEFINE_HEAPENTRY_WITH_CLEANUP(clone, 16, 4, _clone_cleanup) +DEFINE_HEAPENTRY_WITH_CLEANUP(hunk, 4, 4, _hunk_cleanup) + +#define DEFINE_ALLOC_DEALLOC(STATIC, TYPE, SEGTYPE, PLURAL) \ +STATIC TYPE##_t * \ +sm_alloc_##TYPE(seg_manager_t *self, reg_t *addr) \ +{ \ + mem_obj_t *mobj; \ + TYPE##_table_t *table; \ + int offset; \ + \ + if (!self->TYPE##s_seg_id) { \ + mobj = alloc_nonscript_segment(self, SEGTYPE, &(self->TYPE##s_seg_id)); \ + init_##TYPE##_table(&(mobj->data.PLURAL)); \ + } else \ + mobj = self->heap[self->TYPE##s_seg_id]; \ + \ + table = &(mobj->data.PLURAL); \ + offset = alloc_##TYPE##_entry(table); \ + \ + *addr = make_reg(self->TYPE##s_seg_id, offset); \ + return &(mobj->data.PLURAL.table[offset].entry); \ +} \ + \ +STATIC void \ +sm_free_##TYPE(seg_manager_t *self, reg_t addr) \ +{ \ + mem_obj_t *mobj = GET_SEGMENT(*self, addr.segment, SEGTYPE); \ + \ + if (!mobj) { \ + sciprintf("Attempt to free " #TYPE " from address "PREG \ + ": Invalid segment type\n", \ + PRINT_REG(addr)); \ + return; \ + } \ + \ + free_##TYPE##_entry(&(mobj->data.PLURAL), addr.offset); \ +} + +DEFINE_ALLOC_DEALLOC(, clone, MEM_OBJ_CLONES, clones) +DEFINE_ALLOC_DEALLOC(, list, MEM_OBJ_LISTS, lists) +DEFINE_ALLOC_DEALLOC(, node, MEM_OBJ_NODES, nodes) +DEFINE_ALLOC_DEALLOC(static, hunk, MEM_OBJ_HUNK, hunks) + + + +byte * +sm_dereference(seg_manager_t *self, reg_t pointer, int *size) +{ + mem_obj_t *mobj; + byte *base = NULL; + int count; + + if (!pointer.segment + || (pointer.segment >= self->heap_size) + || !self->heap[pointer.segment]) { + sciprintf("Error: Attempt to dereference invalid pointer "PREG"!\n", + PRINT_REG(pointer)); + return NULL; /* Invalid */ + } + + + mobj = self->heap[pointer.segment]; + + switch (mobj->type) { + + case MEM_OBJ_SCRIPT: + if (pointer.offset > mobj->data.script.buf_size) { + sciprintf("Error: Attempt to dereference invalid pointer "PREG + " into script segment (script size=%d)\n", + PRINT_REG(pointer), mobj->data.script.buf_size); + return NULL; + } + if (size) + *size = mobj->data.script.buf_size - pointer.offset; + return (byte *) (mobj->data.script.buf + pointer.offset); + break; + + case MEM_OBJ_LOCALS: + count = mobj->data.locals.nr * sizeof(reg_t); + base = (byte *) mobj->data.locals.locals; + break; + + case MEM_OBJ_STACK: + count = mobj->data.stack.nr * sizeof(reg_t); + base = (byte *) mobj->data.stack.entries; + break; + + case MEM_OBJ_DYNMEM: + count = mobj->data.dynmem.size; + base = (byte *) mobj->data.dynmem.buf; + break; + + case MEM_OBJ_SYS_STRINGS: + if (size) + *size = mobj->data.sys_strings.strings[pointer.offset].max_size; + if (pointer.offset < SYS_STRINGS_MAX + && mobj->data.sys_strings.strings[pointer.offset].name) + return (byte *) (mobj->data.sys_strings.strings[pointer.offset].value); + else { + sciprintf("Error: Attempt to dereference invalid pointer "PREG"!\n", + PRINT_REG(pointer)); + return NULL; + } + + case MEM_OBJ_RESERVED: + sciprintf("Error: Trying to dereference pointer "PREG" to reserved segment `%s'!\n", + mobj->data.reserved); + return NULL; + break; + + default: + sciprintf("Error: Trying to dereference pointer "PREG" to inappropriate" + " segment!\n", + PRINT_REG(pointer)); + return NULL; + } + + if (size) + *size = count; + + return + base + pointer.offset; +} + + +unsigned char * +sm_alloc_dynmem(seg_manager_t *self, int size, const char *descr, reg_t *addr) +{ + seg_id_t seg; + mem_obj_t *mobj = alloc_nonscript_segment(self, MEM_OBJ_DYNMEM, &seg); + *addr = make_reg(seg, 0); + + mobj->data.dynmem.size = size; + + if (size == 0) + mobj->data.dynmem.buf = NULL; + else + mobj->data.dynmem.buf = (byte*) sci_malloc(size); + + mobj->data.dynmem.description = descr; + + return (unsigned char *) (mobj->data.dynmem.buf); +} + +const char * +sm_get_description(seg_manager_t *self, reg_t addr) +{ + mem_obj_t *mobj = self->heap[addr.segment]; + + if (addr.segment >= self->heap_size) + return ""; + + switch (mobj->type) + { + case MEM_OBJ_DYNMEM: + return mobj->data.dynmem.description; + default: + return ""; + } +} + +int +sm_free_dynmem(seg_manager_t *self, reg_t addr) +{ + + if (addr.segment <= 0 + || addr.segment >= self->heap_size + || !self->heap[addr.segment] + || self->heap[addr.segment]->type != MEM_OBJ_DYNMEM) + return 1; /* error */ + + _sm_deallocate(self, addr.segment, 1); + return 0; /* OK */ +} + + +/************************************************************/ +/* ------------------- Segment interface ------------------ */ +/************************************************************/ + + +static void +free_at_address_stub (seg_interface_t *self, reg_t sub_addr) +{ +// sciprintf(" Request to free "PREG"\n", PRINT_REG(sub_addr)); + /* STUB */ +} + + + +static reg_t +find_canonic_address_base (seg_interface_t *self, reg_t addr) +{ + addr.offset = 0; + return addr; +} + +static reg_t +find_canonic_address_id (seg_interface_t *self, reg_t addr) +{ + return addr; +} + +static void +free_at_address_nop (seg_interface_t *self, reg_t sub_addr) +{ +} + +static void +list_all_deallocatable_nop (seg_interface_t *self, void *param, void (*note) (void*param, reg_t addr)) +{ +} + +static void +list_all_deallocatable_base (seg_interface_t *self, void *param, void (*note) (void*param, reg_t addr)) +{ + (*note) (param, make_reg (self->seg_id, 0)); +} + +static void +list_all_outgoing_references_nop (seg_interface_t *self, state_t *s, reg_t addr, void *param, void (*note) (void*param, reg_t addr)) +{ +} + +static void +deallocate_self (seg_interface_t *self) +{ + sci_free (self); +} + + +static void +free_at_address_script (seg_interface_t *self, reg_t addr) +{ + script_t *script; + VERIFY(self->mobj->type == MEM_OBJ_SCRIPT, "Trying to free a non-script!"); + script = &(self->mobj->data.script); +/* + sciprintf("[GC] Freeing script "PREG"\n", PRINT_REG(addr)); + if (script->locals_segment) + sciprintf("[GC] Freeing locals %04x:0000\n", script->locals_segment); +*/ + + if (script->marked_as_deleted) + sm_deallocate_script(self->segmgr, script->nr); +} + +static void +list_all_outgoing_references_script (seg_interface_t *self, state_t *s, reg_t addr, void *param, void (*note) (void*param, reg_t addr)) +{ + script_t *script = &(self->mobj->data.script); + + if (addr.offset <= script->buf_size + && addr.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET + && RAW_IS_OBJECT(script->buf + addr.offset)) { + int idx = RAW_GET_CLASS_INDEX(script, addr); + if (idx >= 0 && idx < script->objects_nr) { + object_t *obj = script->objects + idx; + int i; + + /* Note all local variables, if we have a local variable environment */ + if (script->locals_segment) + (*note) (param, make_reg(script->locals_segment, 0)); + + for (i = 0; i < obj->variables_nr; i++) + (*note) (param, obj->variables[i]); + } else { + fprintf(stderr, "Request for outgoing script-object reference at "PREG" yielded invalid index %d\n", PRINT_REG(addr), idx); + } + } else { +/* fprintf(stderr, "Unexpected request for outgoing script-object references at "PREG"\n", PRINT_REG(addr));*/ + /* Happens e.g. when we're looking into strings */ + } +} + +/*-------------------- script --------------------*/ +static seg_interface_t seg_interface_script = { + /* segmgr = */ NULL, + /* mobj = */ NULL, + /* seg_id = */ 0, + /* type_id = */ MEM_OBJ_SCRIPT, + /* type = */ "script", + /* find_canonic_address = */ find_canonic_address_base, + /* free_at_address = */ free_at_address_script, + /* list_all_deallocatable = */ list_all_deallocatable_base, + /* list_all_outgoing_references = */ list_all_outgoing_references_script, + /* deallocate_self = */ deallocate_self +}; + + +#define LIST_ALL_DEALLOCATABLE(kind, kind_field) \ + mem_obj_t *mobj = self->mobj; \ + kind##_table_t * table = &(mobj->data.kind_field); \ + int i; \ + \ + for (i = 0; i < table->max_entry; i++) \ + if (ENTRY_IS_VALID(table, i)) \ + (*note) (param, make_reg(self->seg_id, i)); + +static void +list_all_deallocatable_clones (seg_interface_t *self, void *param, void (*note) (void*param, reg_t addr)) +{ + LIST_ALL_DEALLOCATABLE(clone, clones); +} + +static void +list_all_outgoing_references_clones (seg_interface_t *self, state_t *s, reg_t addr, void *param, void (*note) (void*param, reg_t addr)) +{ + mem_obj_t *mobj = self->mobj; + clone_table_t *clone_table = &(mobj->data.clones); + clone_t *clone; + int i; + + assert (addr.segment == self->seg_id); + + if (!(ENTRY_IS_VALID(clone_table, addr.offset))) { + fprintf(stderr, "Unexpected request for outgoing references from clone at "PREG"\n", PRINT_REG(addr)); +// BREAKPOINT(); + return; + } + + clone = &(clone_table->table[addr.offset].entry); + + /* Emit all member variables (including references to the 'super' delegate) */ + for (i = 0; i < clone->variables_nr; i++) + (*note) (param, clone->variables[i]); + + /* Note that this also includes the 'base' object, which is part of the script and therefore also + ** emits the locals. */ + (*note) (param, clone->pos); +// sciprintf("[GC] Reporting clone-pos "PREG"\n", PRINT_REG(clone->pos)); +} + + +void +free_at_address_clones(seg_interface_t *self, reg_t addr) +{ + object_t *victim_obj; + + assert (addr.segment == self->seg_id); + + victim_obj = &(self->mobj->data.clones.table[addr.offset].entry); + +#ifdef GC_DEBUG + if (!(victim_obj->flags & OBJECT_FLAG_FREED)) + sciprintf("[GC] Warning: Clone "PREG" not reachable and not freed (freeing now)\n", + PRINT_REG(addr)); +# ifdef GC_DEBUG_VERBOSE + else + sciprintf("[GC-DEBUG] Clone "PREG": Freeing\n", PRINT_REG(addr)); +# endif +#endif +/* + sciprintf("[GC] Clone "PREG": Freeing\n", PRINT_REG(addr)); + sciprintf("[GC] Clone had pos "PREG"\n", PRINT_REG(victim_obj->pos)); +*/ + sci_free(victim_obj->variables); + victim_obj->variables = NULL; + sm_free_clone(self->segmgr, addr); +} + +/*-------------------- clones --------------------*/ +static seg_interface_t seg_interface_clones = { + /* segmgr = */ NULL, + /* mobj = */ NULL, + /* seg_id = */ 0, + /* type_id = */ MEM_OBJ_CLONES, + /* type = */ "clones", + /* find_canonic_address = */ find_canonic_address_id, + /* free_at_address = */ free_at_address_clones, + /* list_all_deallocatable = */ list_all_deallocatable_clones, + /* list_all_outgoing_references = */ list_all_outgoing_references_clones, + /* deallocate_self = */ deallocate_self +}; + + +static reg_t +find_canonic_address_locals (seg_interface_t *self, reg_t addr) +{ + local_variables_t *locals = &(self->mobj->data.locals); + /* Reference the owning script */ + seg_id_t owner_seg = sm_seg_get(self->segmgr, locals->script_id); + + assert (owner_seg >= 0); + + return make_reg(owner_seg, 0); +} + +static void +list_all_outgoing_references_locals (seg_interface_t *self, state_t *s, reg_t addr, void *param, void (*note) (void*param, reg_t addr)) +{ + local_variables_t *locals = &(self->mobj->data.locals); + int i; + + assert (addr.segment == self->seg_id); + + for (i = 0; i < locals->nr; i++) + (*note) (param, locals->locals[i]); +} + +/*-------------------- locals --------------------*/ +static seg_interface_t seg_interface_locals = { + /* segmgr = */ NULL, + /* mobj = */ NULL, + /* seg_id = */ 0, + /* type_id = */ MEM_OBJ_LOCALS, + /* type = */ "locals", + /* find_canonic_address = */ find_canonic_address_locals, + /* free_at_address = */ free_at_address_stub, + /* list_all_deallocatable = */ list_all_deallocatable_nop, + /* list_all_outgoing_references = */ list_all_outgoing_references_locals, + /* deallocate_self = */ deallocate_self +}; + + +static void +list_all_outgoing_references_stack (seg_interface_t *self, state_t *s, reg_t addr, void *param, void (*note) (void*param, reg_t addr)) +{ + int i; +fprintf(stderr, "Emitting %d stack entries\n", self->mobj->data.stack.nr); + for (i = 0; i < self->mobj->data.stack.nr; i++) + (*note) (param, self->mobj->data.stack.entries[i]); +fprintf(stderr, "DONE"); +} + +/*-------------------- stack --------------------*/ +static seg_interface_t seg_interface_stack = { + /* segmgr = */ NULL, + /* mobj = */ NULL, + /* seg_id = */ 0, + /* type_id = */ MEM_OBJ_STACK, + /* type = */ "stack", + /* find_canonic_address = */ find_canonic_address_base, + /* free_at_address = */ free_at_address_nop, + /* list_all_deallocatable = */ list_all_deallocatable_nop, + /* list_all_outgoing_references = */ list_all_outgoing_references_stack, + /* deallocate_self = */ deallocate_self +}; + +/*-------------------- system strings --------------------*/ +static seg_interface_t seg_interface_sys_strings = { + /* segmgr = */ NULL, + /* mobj = */ NULL, + /* seg_id = */ 0, + /* type_id = */ MEM_OBJ_SYS_STRINGS, + /* type = */ "system strings", + /* find_canonic_address = */ find_canonic_address_id, + /* free_at_address = */ free_at_address_nop, + /* list_all_deallocatable = */ list_all_deallocatable_nop, + /* list_all_outgoing_references = */ list_all_outgoing_references_nop, + /* deallocate_self = */ deallocate_self +}; + +static void +list_all_deallocatable_list (seg_interface_t *self, void *param, void (*note) (void*param, reg_t addr)) +{ + LIST_ALL_DEALLOCATABLE(list, lists); +} + +static void +list_all_outgoing_references_list (seg_interface_t *self, state_t *s, reg_t addr, void *param, void (*note) (void*param, reg_t addr)) +{ + list_table_t *table = &(self->mobj->data.lists); + list_t *list = &(table->table[addr.offset].entry); + + if (!ENTRY_IS_VALID(table, addr.offset)) { + fprintf(stderr, "Invalid list referenced for outgoing references: "PREG"\n", PRINT_REG(addr)); + return; + } + + note (param, list->first); + note (param, list->last); + /* We could probably get away with just one of them, but + ** let's be conservative here. */ +} + +static void +free_at_address_lists (seg_interface_t *self, reg_t sub_addr) +{ + sm_free_list (self->segmgr, sub_addr); +} + +/*-------------------- lists --------------------*/ +static seg_interface_t seg_interface_lists = { + /* segmgr = */ NULL, + /* mobj = */ NULL, + /* seg_id = */ 0, + /* type_id = */ MEM_OBJ_LISTS, + /* type = */ "lists", + /* find_canonic_address = */ find_canonic_address_id, + /* free_at_address = */ free_at_address_lists, + /* list_all_deallocatable = */ list_all_deallocatable_list, + /* list_all_outgoing_references = */ list_all_outgoing_references_list, + /* deallocate_self = */ deallocate_self +}; + +static void +list_all_deallocatable_nodes (seg_interface_t *self, void *param, void (*note) (void*param, reg_t addr)) +{ + LIST_ALL_DEALLOCATABLE(node, nodes); +} + +static void +list_all_outgoing_references_nodes (seg_interface_t *self, state_t *s, reg_t addr, void *param, void (*note) (void*param, reg_t addr)) +{ + node_table_t *table = &(self->mobj->data.nodes); + node_t *node = &(table->table[addr.offset].entry); + + if (!ENTRY_IS_VALID(table, addr.offset)) { + fprintf(stderr, "Invalid node referenced for outgoing references: "PREG"\n", PRINT_REG(addr)); + return; + } + + /* We need all four here. Can't just stick with 'pred' OR 'succ' because node operations allow us + ** to walk around from any given node */ + note (param, node->pred); + note (param, node->succ); + note (param, node->key); + note (param, node->value); +} + +static void +free_at_address_nodes (seg_interface_t *self, reg_t sub_addr) +{ + sm_free_node (self->segmgr, sub_addr); +} + +/*-------------------- nodes --------------------*/ +static seg_interface_t seg_interface_nodes = { + /* segmgr = */ NULL, + /* mobj = */ NULL, + /* seg_id = */ 0, + /* type_id = */ MEM_OBJ_NODES, + /* type = */ "nodes", + /* find_canonic_address = */ find_canonic_address_id, + /* free_at_address = */ free_at_address_nodes, + /* list_all_deallocatable = */ list_all_deallocatable_nodes, + /* list_all_outgoing_references = */ list_all_outgoing_references_nodes, + /* deallocate_self = */ deallocate_self +}; + +static void +list_all_deallocatable_hunk (seg_interface_t *self, void *param, void (*note) (void*param, reg_t addr)) +{ + LIST_ALL_DEALLOCATABLE(hunk, hunks); +} + +/*-------------------- hunk --------------------*/ +static seg_interface_t seg_interface_hunk = { + /* segmgr = */ NULL, + /* mobj = */ NULL, + /* seg_id = */ 0, + /* type_id = */ MEM_OBJ_HUNK, + /* type = */ "hunk", + /* find_canonic_address = */ find_canonic_address_id, + /* free_at_address = */ free_at_address_stub, + /* list_all_deallocatable = */ list_all_deallocatable_hunk, + /* list_all_outgoing_references = */ list_all_outgoing_references_nop, + /* deallocate_self = */ deallocate_self +}; + + + +/*-------------------- dynamic memory --------------------*/ +static seg_interface_t seg_interface_dynmem = { + /* segmgr = */ NULL, + /* mobj = */ NULL, + /* seg_id = */ 0, + /* type_id = */ MEM_OBJ_DYNMEM, + /* type = */ "dynamic memory", + /* find_canonic_address = */ find_canonic_address_base, + /* free_at_address = */ free_at_address_stub, + /* list_all_deallocatable = */ list_all_deallocatable_base, + /* list_all_outgoing_references = */ list_all_outgoing_references_nop, + /* deallocate_self = */ deallocate_self +}; + +/*-------------------- reserved --------------------*/ +static seg_interface_t seg_interface_reserved = { + /* segmgr = */ NULL, + /* mobj = */ NULL, + /* seg_id = */ 0, + /* type_id = */ MEM_OBJ_RESERVED, + /* type = */ "reserved", + /* find_canonic_address = */ find_canonic_address_id, + /* free_at_address = */ free_at_address_nop, + /* list_all_deallocatable = */ list_all_deallocatable_nop, + /* list_all_outgoing_references = */ list_all_outgoing_references_nop, + /* deallocate_self = */ deallocate_self +}; + + +static seg_interface_t* seg_interfaces[MEM_OBJ_MAX] = { + &seg_interface_script, + &seg_interface_clones, + &seg_interface_locals, + &seg_interface_stack, + &seg_interface_sys_strings, + &seg_interface_lists, + &seg_interface_nodes, + &seg_interface_hunk, + &seg_interface_dynmem, + &seg_interface_reserved +}; + + +seg_interface_t * +get_seg_interface(seg_manager_t *self, seg_id_t segid) +{ + mem_obj_t *mobj; + seg_interface_t *retval; + + if (!sm_check(self, segid)) + return NULL; /* Invalid segment */ + + mobj = self->heap[segid]; + retval = (seg_interface_t*)sci_malloc(sizeof(seg_interface_t)); + memcpy(retval, seg_interfaces[mobj->type - 1], sizeof (seg_interface_t)); + + if (mobj->type != retval->type_id) { + fprintf(stderr, "Improper segment interface for %d", mobj->type ); + exit(1); + } + + retval->segmgr = self; + retval->mobj = mobj; + retval->seg_id = segid; + + return retval; +} diff --git a/engines/sci/engine/simplesaid.c b/engines/sci/engine/simplesaid.c new file mode 100644 index 0000000000..e74a3aec90 --- /dev/null +++ b/engines/sci/engine/simplesaid.c @@ -0,0 +1,312 @@ +/*************************************************************************** + simplesaid.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + + +#ifdef SCI_SIMPLE_SAID_CODE + +#include +#include + +static int current_pword; +static int said_pos; +static int refstart_pos, parse_pos; +static parse_tree_node_t *words; +static int dontclaim; /* Whether the Parse() event should be claimed */ +static int reached_end; + +static state_t *state; +static heap_ptr addr; + +#define WORDP_MODE_STAY 0 +#define WORDP_MODE_NEXT 1 +#define WORDP_MODE_SEEK 2 + +/* hack */ +#define s state + +static inline int /* Returns whether the specified word matches */ +word_p(int mode, int word) +{ + int oldpp = parse_pos; + do { + if (mode) + ++parse_pos; + + current_pword = words[parse_pos << 1].content.value; + /* SDEBUG("Word at %d = %03x\n", parse_pos, current_pword); */ + if (current_pword == word) { + /* ++matches; */ + SCIkdebug(SCIkSAID, "wordp(%d, %03x) from %d -> 1\n", mode, word, oldpp); + + reached_end |= (words[(parse_pos + 1) << 1].type == -1); + + return 1; + } + + } while ((mode == WORDP_MODE_SEEK) && (words[parse_pos <<1].type != -1)); + + if (words[parse_pos << 1].type == -1) + --parse_pos; + + SCIkdebug(SCIkSAID, "wordp(%d, %03x) from %d -> 0\n", mode, word, oldpp); + + return 0; +} + + + +static inline int /* End of input? */ +end_p() +{ + int val = (words[(parse_pos + 1) << 1 ].type == -1) || reached_end; + + SCIkdebug(SCIkSAID, "endp(pp=%d) = %d\n", parse_pos, val); + + return val; +} + + + +static inline int /* Returns whether the current_word references that specified word */ +reference_p(int referenced_word_index, int word) +{ + int val = 0; + int seeker = (refstart_pos << 1) + 2; + + while (words[seeker].type != -1) { + + if (words[seeker].content.value == word) + if (((words[seeker + 1].content.branches[0] > -1) + && (words[seeker + 1].content.branches[0] == referenced_word_index)) + || ((words[seeker + 1].content.branches[1] > -1) + && (words[seeker + 1].content.branches[1] == referenced_word_index))) { + val = 1; + reached_end |= (words[seeker + 2].type == -1); + break; + } + + seeker += 2; + } + + SCIkdebug(SCIkSAID, "%03x > pos[%d] = %d (start at %d)\n", word, referenced_word_index, val, refstart_pos); + + return val; +} + + +static inline int /* Checks whether the current word has trailing references */ +follows_p(void) +{ + /* int val = (words[(parse_pos << 1) + 1].content.branches[1] > (parse_pos << 1)); + + SDEBUG("follows-p(pp=%d) = %d\n", parse_pos, val);*/ + + dontclaim = 1; + return 1; /* appears to work best */ +} + + +static inline int +next_parse_token(int *token_is_op) +{ + int item = state->heap[addr++]; + + if (item < 0xf0) { + item = item << 8 | state->heap[addr++]; + *token_is_op = 0; + } else + *token_is_op = 1; + + return item; +} + +#define STATE_INITIAL 0 +#define STATE_OR 1 +#define STATE_SEEK 2 +#define STATE_REF 3 + +static int +simplesaid(int minor_state, int terminator) +{ + int major_state = STATE_INITIAL; + int refword = parse_pos; /* The word references apply to */ + int aspiring_refword = -1; /* in "a < b < c", c refers to b, and aspiring_refword will be b. */ + int truth = 1; + int token_is_op; + SCIkdebug(SCIkSAID, "simplesaid(%02x)\n", terminator); + + while (42) { + + int token = next_parse_token(&token_is_op); + SCIkdebug(SCIkSAID, "Got token %03x on truth %d\n", token, truth); + + if (token_is_op && (token == terminator)) { + if (terminator == SAID_TERM) + truth = truth && end_p(); + SCIkdebug(SCIkSAID, "Terminator; returning %d\n", truth); + return truth; + } + + if (token_is_op) /* operator? */ + switch (token) { + + case SAID_COMMA: + minor_state = STATE_OR; + break; + + case SAID_SLASH: + if (!truth) { + while (((token = next_parse_token(&token_is_op)) != terminator) + && (token != SAID_TERM)); /* Proceed to end of block */ + if (token != terminator) + SCIkwarn(SCIkERROR, "Syntax error: Unexpected end of spec"); + return 0; + } + + major_state = STATE_SEEK; + minor_state = STATE_INITIAL; + break; + + case SAID_PARENC: + SCIkwarn(SCIkERROR, "')' without matching '('\n"); + return 0; + + case SAID_PARENO: + switch (minor_state) { + + case STATE_OR: + truth |= simplesaid(minor_state, SAID_PARENC); + break; + + case STATE_INITIAL: + truth = truth && simplesaid(minor_state, SAID_PARENC); + break; + + default: + SCIkwarn(SCIkERROR, "'(' in minor state %d\n", minor_state); + } + break; + + case SAID_BRACKC: + SCIkwarn(SCIkERROR, "']' without matching '['\n"); + return 0; + + case SAID_BRACKO: { + int parse_pos_old = parse_pos; + int recurse = simplesaid(minor_state, SAID_BRACKC); + if (!recurse) + parse_pos = parse_pos_old; + break; + } + + case SAID_LT: + if (aspiring_refword > -1) /* "a < b < c" */ + refword = aspiring_refword; /* refword = 'b' (in the case above) */ + major_state = STATE_REF; + break; + + case SAID_GT: + return truth && follows_p(); + + case SAID_TERM: + SCIkwarn(SCIkERROR, "Unexpected end of spec\n"); + return 0; + + default: + SCIkwarn(SCIkERROR, "Syntax error: Unexpected token 0x%02x\n", token); + return 0; + } else { + int tempresult; + + /* ++word_tokens_nr; /* Normal word token */ + + switch(major_state) { + + case STATE_INITIAL: + tempresult = word_p(((minor_state == STATE_OR)? WORDP_MODE_STAY : WORDP_MODE_NEXT), token); + refword = parse_pos; + break; + + case STATE_SEEK: + tempresult = word_p(WORDP_MODE_SEEK, token); + major_state = STATE_INITIAL; + refword = parse_pos; + break; + + case STATE_REF: + tempresult = reference_p(refword, token); + aspiring_refword = parse_pos; + break; + + default: + SCIkwarn(SCIkERROR, "Invalid major state!\n"); + return 0; + } + + SCIkdebug(SCIkSAID, "state=(%d,%d), truth = %d * %d\n", major_state, minor_state, truth, tempresult); + + if (minor_state == STATE_OR) + truth |= tempresult; + else + truth = truth && tempresult; + + minor_state = STATE_INITIAL; + } + } +} + +#undef s + +int +vocab_simple_said_test(state_t *s, heap_ptr address) +{ + int matched; + current_pword = reached_end = 0; + dontclaim = 0; + said_pos = 0; + if (s->parser_lastmatch_word == SAID_NO_MATCH) + SCIkdebug(SCIkSAID, "lastmatch_word: none\n"); + else + SCIkdebug(SCIkSAID, "lastmatch_word=%d\n", s->parser_lastmatch_word); + parse_pos = (s->parser_lastmatch_word == SAID_NO_MATCH)? -1 : s->parser_lastmatch_word; + refstart_pos = parse_pos; + state = s; + addr = address; + words = s->parser_nodes; + matched = simplesaid(STATE_INITIAL, SAID_TERM); + SCIkdebug(SCIkSAID, "Result: (matched,dontclaim)=(%d,%d)\n", matched, dontclaim); + + if (!matched) + return SAID_NO_MATCH; + + if (dontclaim) /* partial match */ + return parse_pos; /* Return lastmatch word */ + + return SAID_FULL_MATCH; +} + +#endif /* SCI_SIMPLE_SAID_CODE */ diff --git a/engines/sci/engine/sys_strings.c b/engines/sci/engine/sys_strings.c new file mode 100644 index 0000000000..945d79343c --- /dev/null +++ b/engines/sci/engine/sys_strings.c @@ -0,0 +1,119 @@ +/*************************************************************************** + sys_strings.c Copyright (C) 2002 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include +#include + +void +sys_string_acquire(sys_strings_t *strings, int index, const char *name, int max_len) +{ + sys_string_t *str = strings->strings + index; + + if (index < 0 || index >= SYS_STRINGS_MAX) { + fprintf(stderr, "[SYSSTR] Error: Attempt to acquire string #%d\n", + index); + BREAKPOINT(); + } + + if (str->name + && (strcmp(name, str->name) + || (str->max_size != max_len))) { + fprintf(stderr, "[SYSSTR] Error: Attempt to re-acquire existing string #%d;" + "was '%s', tried to claim as '%s'\n", + index, str->name, name); + BREAKPOINT(); + } + + str->name = strdup(name); + str->max_size = max_len; + str->value = (char*)sci_malloc(max_len + 1); + str->value[0] = 0; /* Set to empty string */ +} + +int +sys_string_set(sys_strings_t *strings, int index, const char *value) +{ + sys_string_t *str = strings->strings + index; + + if (index < 0 || index >= SYS_STRINGS_MAX || !str->name) { + fprintf(stderr, "[SYSSTR] Error: Attempt to write to invalid/unused string #%d\n", + index); + BREAKPOINT(); + return 1; + } + + strncpy(str->value, value, str->max_size); + str->value[str->max_size] = 0; /* Make sure to terminate */ + return 0; +} + +void +sys_string_free(sys_strings_t *strings, int index) +{ + sys_string_t *str = strings->strings + index; + + free(str->name); + str->name = NULL; + + free(str->value); + str->value = NULL; + + str->max_size = 0; +} + +void +sys_string_free_all(sys_strings_t *strings) +{ + int i; + + for (i=0;istrings[i].name) + sys_string_free(strings, i); + } + +} + +void +sys_strings_restore(sys_strings_t *new_strings, sys_strings_t *old_strings) +{ + int i; + + /* First, pad memory */ + for (i = 0; i < SYS_STRINGS_MAX; i++) { + sys_string_t *s = new_strings->strings + i; + char *data = s->value; + if (data) { + s->value = (char *)sci_malloc(s->max_size + 1); + strcpy(s->value, data); + sci_free(data); + } + } + + sys_string_set(new_strings, SYS_STRING_SAVEDIR, old_strings->strings[SYS_STRING_SAVEDIR].value); +} diff --git a/engines/sci/engine/vm.c b/engines/sci/engine/vm.c new file mode 100644 index 0000000000..d628e80e7a --- /dev/null +++ b/engines/sci/engine/vm.c @@ -0,0 +1,2410 @@ +/*************************************************************************** + vm.c Copyright (C) 1999 -- 2002 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + + +#include +#include +#include +#include +#include "kernel_types.h" +#include +#include "gc.h" +#include + +#ifdef HAVE_SETJMP_H +#include +#endif + +reg_t NULL_REG = NULL_REG_INITIALIZER; + +/*#define VM_DEBUG_SEND*/ +#undef STRICT_SEND /* Disallows variable sends with more than one parameter */ +#undef STRICT_READ /* Disallows reading from out-of-bounds parameters and locals */ + + +int script_abort_flag = 0; /* Set to 1 to abort execution */ +int script_error_flag = 0; /* Set to 1 if an error occured, reset each round by the VM */ +int script_checkloads_flag = 0; /* Print info when scripts get (un)loaded */ +int script_step_counter = 0; /* Counts the number of steps executed */ +int script_gc_interval = GC_INTERVAL; /* Number of steps in between gcs */ + +extern int _debug_step_running; /* scriptdebug.c */ +extern int _debug_seeking; /* scriptdebug.c */ +extern int _weak_validations; /* scriptdebug.c */ + + +calls_struct_t *send_calls = NULL; +int send_calls_allocated = 0; +int bp_flag = 0; +static reg_t _dummy_register = NULL_REG_INITIALIZER; + + +static int jump_initialized = 0; +#ifdef HAVE_SETJMP_H +static jmp_buf vm_error_address; +#endif + +/*-- validation functionality --*/ + +#ifndef DISABLE_VALIDATIONS + +static inline reg_t * +validate_property(object_t *obj, int index) +{ + if (!obj) + { + if (sci_debug_flags & 4) + sciprintf("[VM] Sending to disposed object!\n"); + _dummy_register = NULL_REG; + return &_dummy_register; + } + + if (index < 0 || index >= obj->variables_nr) { + if (sci_debug_flags & 4) + sciprintf("[VM] Invalid property #%d (out of [0..%d]) requested!\n", index, + obj->variables_nr); + + _dummy_register = NULL_REG; + return &_dummy_register; + } + + return obj->variables + index; +} + +static inline stack_ptr_t +validate_stack_addr(state_t *s, stack_ptr_t sp) +{ + if (sp >= s->stack_base && sp < s->stack_top) + return sp; + + script_debug_flag = script_error_flag = 1; + if (sci_debug_flags & 4) + sciprintf("[VM] Stack index %d out of valid range [%d..%d]\n", + sp - s->stack_base, 0, s->stack_top - s->stack_base -1); + return 0; +} + + +static inline int +validate_arithmetic(reg_t reg) +{ + if (reg.segment) { + if (!_weak_validations) + script_debug_flag = script_error_flag = 1; + if (sci_debug_flags & 4) + sciprintf("[VM] Attempt to read arithmetic value from non-zero segment [%04x]\n", reg.segment); + return 0; + } + + return reg.offset; +} + +static inline int +signed_validate_arithmetic(reg_t reg) +{ + if (reg.segment) { + if (!_weak_validations) + script_debug_flag = script_error_flag = 1; + if (sci_debug_flags & 4) + sciprintf("[VM] Attempt to read arithmetic value from non-zero segment [%04x]\n", reg.segment); + return 0; + } + + if (reg.offset&0x8000) + return (signed) (reg.offset)-65536; + else + return reg.offset; +} + +static inline int +validate_variable(reg_t *r, reg_t *stack_base, int type, int max, int index, int line) +{ + const char *names[4] = {"global", "local", "temp", "param"}; + + if (index < 0 || index >= max) { + sciprintf("[VM] Attempt to use invalid %s variable %04x ", names[type], index); + if (max == 0) + sciprintf("(variable type invalid)"); + else + sciprintf("(out of range [%d..%d])", 0, max-1); + sciprintf(" in %s, line %d\n", __FILE__, line); + if (!_weak_validations) + script_debug_flag = script_error_flag = 1; + +#ifdef STRICT_READ + return 1; +#else /* !STRICT_READ */ + if (type == VAR_PARAM || type == VAR_TEMP) { + int total_offset = r - stack_base; + if (total_offset < 0 || total_offset >= VM_STACK_SIZE) { + sciprintf("[VM] Access would be outside even of the stack (%d); access denied\n", + total_offset); + return 1; + } else { + sciprintf("[VM] Access within stack boundaries; access granted.\n"); + return 0; + } + }; +#endif + } + + return 0; +} + +static inline reg_t +validate_read_var(reg_t *r, reg_t *stack_base, int type, int max, int index, int line, reg_t default_value) +{ + if (!validate_variable(r, stack_base, type, max, index, line)) + return r[index]; + else + return default_value; +} + +static inline void +validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, int index, int line, reg_t value) +{ + if (!validate_variable(r, stack_base, type, max, index, line)) + r[index] = value; +} + +# define ASSERT_ARITHMETIC(v) validate_arithmetic(v) + +#else +/*-- Non-validating alternatives -- */ + +# define validate_stack_addr(s, sp) sp +# define validate_arithmetic(r) ((r).offset) +# define signed_validate_arithmetic(r) ((int) ((r).offset)&0x8000 ? (signed) ((r).offset)-65536 : ((r).offset)) +# define validate_variable(r, sb, t, m, i, l) +# define validate_read_var(r, sb, t, m, i, l) ((r)[i]) +# define validate_write_var(r, sb, t, m, i, l, v) ((r)[i] = (v)) +# define validate_property(o, p) (&((o)->variables[p])) +# define ASSERT_ARITHMETIC(v) (v).offset + +#endif + +#define READ_VAR(type, index, def) validate_read_var(variables[type], s->stack_base, type, variables_max[type], index, __LINE__, def) +#define WRITE_VAR(type, index, value) validate_write_var(variables[type], s->stack_base, type, variables_max[type], index, __LINE__, value) +#define WRITE_VAR16(type, index, value) WRITE_VAR(type, index, make_reg(0, value)); + +#define ACC_ARITHMETIC_L(op) make_reg(0, (op validate_arithmetic(s->r_acc))) +#define ACC_AUX_LOAD() aux_acc = signed_validate_arithmetic(s->r_acc) +#define ACC_AUX_STORE() s->r_acc = make_reg(0, aux_acc) + +#define OBJ_PROPERTY(o, p) (*validate_property(o, p)) + +/*==--------------------------==*/ + +int +script_error(state_t *s, const char *file, int line, const char *reason) +{ + sciprintf("Script error in file %s, line %d: %s\n", file, line, reason); + script_debug_flag = script_error_flag = 1; + return 0; +} +#define CORE_ERROR(area, msg) script_error(s, "[" area "] " __FILE__, __LINE__, msg) + +reg_t +get_class_address(state_t *s, int classnr, int lock, reg_t caller) +{ + class_t *class = s->classtable + classnr; + + if (NULL == s) { + sciprintf("vm.c: get_class_address(): NULL passed for \"s\"\n"); + return NULL_REG; + } + + if (classnr < 0 + || s->classtable_size <= classnr + || class->script < 0) { + sciprintf("[VM] Attempt to dereference class %x, which doesn't exist (max %x)\n", + classnr, s->classtable_size); + script_error_flag = script_debug_flag = 1; + return NULL_REG; + } else { + if (!class->reg.segment) { + script_get_segment(s, class->script, lock); + + if (!class->reg.segment) { + sciprintf("[VM] Trying to instantiate class %x by instantiating script 0x%x (%03d) failed;" + " Entering debugger.\n", classnr, class->script); + script_error_flag = script_debug_flag = 1; + return NULL_REG; + } + } else + if (caller.segment != class->reg.segment) + sm_increment_lockers(&s->seg_manager, class->reg.segment, SEG_ID); + + return class->reg; + } +} + +/* Operating on the stack */ +/* 16 bit: */ +#define PUSH(v) PUSH32(make_reg(0, v)) +#define POP() (validate_arithmetic(POP32())) +/* 32 bit: */ +#define PUSH32(a) (*(validate_stack_addr(s, (xs->sp)++)) = (a)) +#define POP32() (*(validate_stack_addr(s, --(xs->sp)))) + +/* Getting instruction parameters */ +#define GET_OP_BYTE() ((guint8) code_buf[(xs->addr.pc.offset)++]) +#define GET_OP_WORD() (getUInt16(code_buf + ((xs->addr.pc.offset) += 2) - 2)) +#define GET_OP_FLEX() ((opcode & 1)? GET_OP_BYTE() : GET_OP_WORD()) +#define GET_OP_SIGNED_BYTE() ((gint8)(code_buf[(xs->addr.pc.offset)++])) +#define GET_OP_SIGNED_WORD() ((getInt16(code_buf + ((xs->addr.pc.offset) += 2) - 2))) +#define GET_OP_SIGNED_FLEX() ((opcode & 1)? GET_OP_SIGNED_BYTE() : GET_OP_SIGNED_WORD()) + +#define SEG_GET_HEAP( s, reg ) sm_get_heap( &s->seg_manager, reg ) +#define OBJ_SPECIES(s, reg) SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + SCRIPT_SPECIES_OFFSET)) +/* Returns an object's species */ + +#define OBJ_SUPERCLASS(s, reg) SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + SCRIPT_SUPERCLASS_OFFSET)) +/* Returns an object's superclass */ + +inline exec_stack_t * +execute_method(state_t *s, word script, word pubfunct, stack_ptr_t sp, + reg_t calling_obj, word argc, stack_ptr_t argp) +{ + int seg; + guint16 temp; + + if (!sm_script_is_loaded (&s->seg_manager, script, SCRIPT_ID)) /* Script not present yet? */ + script_instantiate(s, script); + else + sm_unmark_script_deleted(&s->seg_manager, script); + + seg = sm_seg_get( &s->seg_manager, script ); + + temp = sm_validate_export_func( &s->seg_manager, pubfunct, seg ); + VERIFY( temp, "Invalid pubfunct in export table" ); + if( !temp ) { + sciprintf("Request for invalid exported function 0x%x of script 0x%x\n", pubfunct, script); + script_error_flag = script_debug_flag = 1; + return NULL; + } + + /* Check if a breakpoint is set on this method */ + if (s->have_bp & BREAK_EXPORT) + { + breakpoint_t *bp; + guint32 bpaddress; + + bpaddress = (script << 16 | pubfunct); + + bp = s->bp_list; + while (bp) + { + if (bp->type == BREAK_EXPORT && bp->data.address == bpaddress) + { + sciprintf ("Break on script %d, export %d\n", script, pubfunct); + script_debug_flag = 1; + bp_flag = 1; + break; + } + bp = bp->next; + } + } + + return add_exec_stack_entry(s, make_reg( seg, temp ), + sp, calling_obj, argc, argp, -1, calling_obj, + s->execution_stack_pos, seg); +} + + +static void +_exec_varselectors(state_t *s) +{ /* Executes all varselector read/write ops on the TOS */ + /* Now check the TOS to execute all varselector entries */ + if (s->execution_stack_pos >= 0) + while (s->execution_stack[s->execution_stack_pos].type == EXEC_STACK_TYPE_VARSELECTOR) { + /* varselector access? */ + if (s->execution_stack[s->execution_stack_pos].argc) { /* write? */ + reg_t temp = s->execution_stack[s->execution_stack_pos].variables_argp[1]; + *(s->execution_stack[s->execution_stack_pos].addr.varp) = temp; + + } else /* No, read */ + s->r_acc = *(s->execution_stack[s->execution_stack_pos].addr.varp); + + --(s->execution_stack_pos); + } +} + +exec_stack_t * +send_selector(state_t *s, reg_t send_obj, reg_t work_obj, + stack_ptr_t sp, int framesize, stack_ptr_t argp) + /* send_obj and work_obj are equal for anything but 'super' */ + /* Returns a pointer to the TOS exec_stack element */ +{ +#ifdef VM_DEBUG_SEND + int i; +#endif + reg_t *varp; + reg_t funcp; + int selector; + int argc; + int origin = s->execution_stack_pos; /* Origin: Used for debugging */ + exec_stack_t *retval = s->execution_stack + s->execution_stack_pos; + int print_send_action = 0; + /* We return a pointer to the new active exec_stack_t */ + + /* The selector calls we catch are stored below: */ + int send_calls_nr = -1; + + if (NULL == s) { + sciprintf("vm.c: exec_stack_t(): NULL passed for \"s\"\n"); + return NULL; + } + + while (framesize > 0) { + + selector = validate_arithmetic(*argp++); + argc = validate_arithmetic(*argp); + + if (argc > 0x800){ /* More arguments than the stack could possibly accomodate for */ + CORE_ERROR("SEND", "More than 0x800 arguments to function call\n"); + return NULL; + } + + /* Check if a breakpoint is set on this method */ + if (s->have_bp & BREAK_SELECTOR) { + breakpoint_t *bp; + char method_name [256]; + + sprintf (method_name, "%s::%s", + obj_get_name(s, send_obj), + s->selector_names [selector]); + + bp = s->bp_list; + while (bp) { + int cmplen = strlen(bp->data.name); + if (bp->data.name[cmplen - 1] != ':') + cmplen = 256; + + if (bp->type == BREAK_SELECTOR && !strncmp (bp->data.name, method_name, cmplen)) { + sciprintf ("Break on %s (in ["PREG"])\n", method_name, + PRINT_REG(send_obj)); + script_debug_flag = print_send_action = 1; + bp_flag = 1; + break; + } + bp = bp->next; + } + } + +#ifdef VM_DEBUG_SEND + sciprintf("Send to "PREG", selector %04x (%s):", + PRINT_REG(send_obj), selector, s->selector_names[selector]); +#endif /* VM_DEBUG_SEND */ + + if (++send_calls_nr == (send_calls_allocated - 1)) + send_calls = (calls_struct_t*)sci_realloc(send_calls, sizeof(calls_struct_t) + * (send_calls_allocated *= 2)); + + + switch (lookup_selector(s, send_obj, selector, &varp, &funcp)) { + + case SELECTOR_NONE: + sciprintf("Send to invalid selector 0x%x of object at "PREG"\n", + 0xffff & selector, PRINT_REG(send_obj)); + script_error_flag = script_debug_flag = 1; + --send_calls_nr; + break; + + case SELECTOR_VARIABLE: + +#ifdef VM_DEBUG_SEND + sciprintf("Varselector: "); + if (argc) + sciprintf("Write "PREG"\n", PRINT_REG(argp[1])); + else + sciprintf("Read\n"); +#endif /* VM_DEBUG_SEND */ + + switch (argc) { + case 0: /* Read selector */ + if (print_send_action) { + sciprintf("[read selector]\n"); + print_send_action = 0; + } + /* fallthrough */ + case 1: +#ifndef STRICT_SEND + default: +#endif + { /* Argument is supplied -> Selector should be set */ + + if (print_send_action) { + reg_t val = *varp; + reg_t new = argp[1]; + + sciprintf("[write to selector: change "PREG" to "PREG"]\n", + PRINT_REG(val), PRINT_REG(new)); + print_send_action = 0; + } + send_calls[send_calls_nr].address.var = varp; /* register the call */ + send_calls[send_calls_nr].argp = argp; + send_calls[send_calls_nr].argc = argc; + send_calls[send_calls_nr].selector = selector; + send_calls[send_calls_nr].type = EXEC_STACK_TYPE_VARSELECTOR; /* Register as a varselector */ + + } break; +#ifdef STRICT_SEND + default: + --send_calls_nr; + sciprintf("Send error: Variable selector %04x in "PREG" called with %04x params\n", + selector, PRINT_REG(send_obj), argc); + script_debug_flag = 1; /* Enter debug mode */ + _debug_seeking = _debug_step_running = 0; + +#endif + } + break; + + case SELECTOR_METHOD: + +#ifdef VM_DEBUG_SEND + sciprintf("Funcselector("); + for (i = 0; i < argc; i++) { + sciprintf(PREG, PRINT_REG(argp[i+1])); + if (i + 1 < argc) + sciprintf(", "); + } + sciprintf(") at "PREG"\n", PRINT_REG(funcp)); +#endif /* VM_DEBUG_SEND */ + if (print_send_action) { + sciprintf("[invoke selector]\n"); + print_send_action = 0; + } + + send_calls[send_calls_nr].address.func = funcp; /* register call */ + send_calls[send_calls_nr].argp = argp; + send_calls[send_calls_nr].argc = argc; + send_calls[send_calls_nr].selector = selector; + send_calls[send_calls_nr].type = EXEC_STACK_TYPE_CALL; + send_calls[send_calls_nr].sp = sp; + sp = CALL_SP_CARRY; /* Destroy sp, as it will be carried over */ + + break; + } /* switch(lookup_selector()) */ + + + framesize -= (2 + argc); + argp += argc + 1; + } + + /* Iterate over all registered calls in the reverse order. This way, the first call is + ** placed on the TOS; as soon as it returns, it will cause the second call to be executed. + */ + for (; send_calls_nr >= 0; send_calls_nr--) + if (send_calls[send_calls_nr].type == EXEC_STACK_TYPE_VARSELECTOR) /* Write/read variable? */ + retval = add_exec_stack_varselector(s, work_obj, send_calls[send_calls_nr].argc, + send_calls[send_calls_nr].argp, + send_calls[send_calls_nr].selector, + send_calls[send_calls_nr].address.var, origin); + + else + retval = + add_exec_stack_entry(s, send_calls[send_calls_nr].address.func, + send_calls[send_calls_nr].sp, work_obj, + send_calls[send_calls_nr].argc, + send_calls[send_calls_nr].argp, + send_calls[send_calls_nr].selector, + send_obj, origin, + SCI_XS_CALLEE_LOCALS); + + _exec_varselectors(s); + + retval = s->execution_stack + s->execution_stack_pos; + return retval; +} + + +exec_stack_t * +add_exec_stack_varselector(state_t *s, reg_t objp, int argc, stack_ptr_t argp, + selector_t selector, reg_t *address, int origin) +{ + exec_stack_t *xstack = add_exec_stack_entry(s, NULL_REG, address, objp, argc, argp, + selector, objp, origin, SCI_XS_CALLEE_LOCALS); + /* Store selector address in sp */ + + xstack->addr.varp = address; + xstack->type = EXEC_STACK_TYPE_VARSELECTOR; + + return xstack; +} + + +exec_stack_t * +add_exec_stack_entry(state_t *s, reg_t pc, stack_ptr_t sp, reg_t objp, int argc, + stack_ptr_t argp, selector_t selector, reg_t sendp, int origin, + seg_id_t locals_segment) +/* Returns new TOS element for the execution stack*/ +/* locals_segment may be -1 if derived from the called object */ +{ + exec_stack_t *xstack = NULL; + + if (!s->execution_stack) + s->execution_stack = + (exec_stack_t*)sci_malloc(sizeof(exec_stack_t) * (s->execution_stack_size = 16)); + + if (++(s->execution_stack_pos) == s->execution_stack_size) /* Out of stack space? */ + s->execution_stack = (exec_stack_t*)sci_realloc(s->execution_stack, + sizeof(exec_stack_t) * (s->execution_stack_size += 8)); + + /* sciprintf("Exec stack: [%d/%d], origin %d, at %p\n", s->execution_stack_pos, + s->execution_stack_size, origin, s->execution_stack); */ + + xstack = s->execution_stack + s->execution_stack_pos; + + xstack->objp = objp; + if (locals_segment != SCI_XS_CALLEE_LOCALS) + xstack->local_segment = locals_segment; + else + xstack->local_segment = pc.segment; + + xstack->sendp = sendp; + xstack->addr.pc = pc; + xstack->fp = xstack->sp = sp; + xstack->argc = argc; + + xstack->variables_argp = argp; /* Parameters */ + + *argp = make_reg(0, argc); /* SCI code relies on the zeroeth argument to equal argc */ + + /* Additional debug information */ + xstack->selector = selector; + xstack->origin = origin; + + xstack->type = EXEC_STACK_TYPE_CALL; /* Normal call */ + + return xstack; +} + + +#ifdef DISABLE_VALIDATONS +# define kernel_matches_signature(a, b, c, d) 1 +#endif + + +void +vm_handle_fatal_error(state_t *s, int line, const char *file) +{ + fprintf(stderr, "Fatal VM error in %s, L%d; aborting...\n", file, line); +#ifdef HAVE_SETJMP_H + if (jump_initialized) + longjmp(vm_error_address, 0); +#endif + fprintf(stderr, "Could not recover, exitting...\n"); + exit(1); +} + +static inline script_t * +script_locate_by_segment(state_t *s, seg_id_t seg) +{ + mem_obj_t *memobj = GET_SEGMENT(s->seg_manager, seg, MEM_OBJ_SCRIPT); + if (memobj) + return &(memobj->data.script); + + return NULL; +} + + +static reg_t +pointer_add(state_t *s, reg_t base, int offset) +{ + mem_obj_t *mobj = GET_SEGMENT_ANY(s->seg_manager, base.segment); + + if (!mobj) { + script_debug_flag = script_error_flag = 1; + sciprintf("[VM] Error: Attempt to add %d to invalid pointer "PREG"!", offset, PRINT_REG(base)); + return NULL_REG; + } + + switch (mobj->type) { + + case MEM_OBJ_LOCALS: + base.offset += 2*offset; + return base; + + case MEM_OBJ_SCRIPT: + case MEM_OBJ_STACK: + case MEM_OBJ_DYNMEM: + base.offset += offset; + return base; + break; + + default: + sciprintf("[VM] Error: Attempt to add %d to pointer "PREG": Pointer arithmetics of this type unsupported!", offset, PRINT_REG(base)); + return NULL_REG; + + } +} + +static inline void +gc_countdown(state_t *s) +{ + if (s->gc_countdown-- <= 0) { + s->gc_countdown = script_gc_interval; + run_gc(s); + } +} + +static byte _fake_return_buffer[2] = {op_ret << 1, op_ret << 1}; + +void +run_vm(state_t *s, int restoring) +{ + reg_t *variables[4]; /* global, local, temp, param, as immediate pointers */ + reg_t *variables_base[4]; /* Used for referencing VM ops */ + seg_id_t variables_seg[4]; /* Same as above, contains segment IDs */ +#ifndef DISABLE_VALIDATIONS + int variables_max[4]; /* Max. values for all variables */ + int code_buf_size = 0 /* (Avoid spurious warning) */; +#endif + int temp; + gint16 aux_acc; /* Auxiliary 16 bit accumulator */ + reg_t r_temp; /* Temporary register */ + stack_ptr_t s_temp; /* Temporary stack pointer */ + gint16 opparams[4]; /* opcode parameters */ + + int restadjust = s->r_amp_rest; /* &rest adjusts the parameter count + ** by this value */ + /* Current execution data: */ + exec_stack_t *xs = s->execution_stack + s->execution_stack_pos; + exec_stack_t *xs_new = NULL /* (Avoid spurious warning) */; /* Used during some operations */ + object_t *obj = obj_get(s, xs->objp); + script_t *local_script = script_locate_by_segment(s, xs->local_segment); + int old_execution_stack_base = s->execution_stack_base; /* Used to detect the + ** stack bottom, for "physical" + ** returns */ + byte *code_buf = NULL /* (Avoid spurious warning) */; + + if (!local_script) { + script_error(s, __FILE__, __LINE__, "Program Counter gone astray"); + return; + } + + if (NULL == s) { + sciprintf("vm.c: run_vm(): NULL passed for \"s\"\n"); + return; + } + +#ifdef HAVE_SETJMP_H + setjmp(vm_error_address); + jump_initialized = 1; +#endif + + if (!restoring) + s->execution_stack_base = s->execution_stack_pos; + +#ifndef DISABLE_VALIDATIONS + /* Initialize maximum variable count */ + if (s->script_000->locals_block) + variables_max[VAR_GLOBAL] = s->script_000->locals_block->nr; + else + variables_max[VAR_GLOBAL] = 0; +#endif + + variables_seg[VAR_GLOBAL] = s->script_000->locals_segment; + variables_seg[VAR_TEMP] = variables_seg[VAR_PARAM] = s->stack_segment; + variables_base[VAR_TEMP] = variables_base[VAR_PARAM] = s->stack_base; + + /* SCI code reads the zeroeth argument to determine argc */ + if (s->script_000->locals_block) + variables_base[VAR_GLOBAL] = variables[VAR_GLOBAL] + = s->script_000->locals_block->locals; + else + variables_base[VAR_GLOBAL] = variables[VAR_GLOBAL] = NULL; + + + + s->execution_stack_pos_changed = 1; /* Force initialization */ + + while (1) { + byte opcode; + int old_pc_offset; + stack_ptr_t old_sp = xs->sp; + byte opnumber; + int var_type; /* See description below */ + int var_number; + + old_pc_offset = xs->addr.pc.offset; + + if (s->execution_stack_pos_changed) { + script_t *scr; + xs = s->execution_stack + s->execution_stack_pos; + s->execution_stack_pos_changed = 0; + + scr = script_locate_by_segment(s, xs->addr.pc.segment); + if (!scr) { + /* No script? Implicit return via fake instruction buffer */ + SCIkdebug(SCIkWARNING, "Running on non-existant script in segment %x!\n", xs->addr.pc.segment); + code_buf = _fake_return_buffer; +#ifndef DISABLE_VALIDATIONS + code_buf_size = 2; +#endif + xs->addr.pc.offset = 1; + + scr = NULL; + obj = NULL; + } else { + obj = obj_get(s, xs->objp); + code_buf = scr->buf; +#ifndef DISABLE_VALIDATIONS + code_buf_size = scr->buf_size; +#endif + /* if (!obj) { + SCIkdebug(SCIkWARNING, "Running with non-existant self= "PREG"\n", PRINT_REG(xs->objp)); + }*/ + + local_script = script_locate_by_segment(s, xs->local_segment); + if (!local_script) { + SCIkdebug(SCIkWARNING, "Could not find local script from segment %x!\n", xs->local_segment); + local_script = NULL; + variables_base[VAR_LOCAL] = variables[VAR_LOCAL] = NULL; +#ifndef DISABLE_VALIDATIONS + variables_max[VAR_LOCAL] = 0; +#endif + } else { + + variables_seg[VAR_LOCAL] = local_script->locals_segment; + if (local_script->locals_block) + variables_base[VAR_LOCAL] = variables[VAR_LOCAL] + = local_script->locals_block->locals; + else + variables_base[VAR_LOCAL] = variables[VAR_LOCAL] + = NULL; +#ifndef DISABLE_VALIDATIONS + if (local_script->locals_block) + variables_max[VAR_LOCAL] = local_script->locals_block->nr; + else + variables_max[VAR_LOCAL] = 0; + variables_max[VAR_TEMP] = xs->sp - xs->fp; + variables_max[VAR_PARAM] = xs->argc + 1; +#endif + } + variables[VAR_TEMP] = xs->fp; + variables[VAR_PARAM] = xs->variables_argp; + } + + } + + script_error_flag = 0; /* Set error condition to false */ + + if (script_abort_flag) + return; /* Emergency */ + + /* Debug if this has been requested: */ + if (script_debug_flag || sci_debug_flags) { + script_debug(s, &(xs->addr.pc), &(xs->sp), &(xs->fp), + &(xs->objp), &restadjust, + variables_seg, variables, variables_base, +#ifdef DISABLE_VALIDATIONS + NULL, +#else + variables_max, +#endif + bp_flag); + bp_flag = 0; + } + +#ifndef DISABLE_VALIDATIONS + if (xs->sp < xs->fp) + script_error(s, "[VM] "__FILE__, __LINE__, "Stack underflow"); + + variables_max[VAR_TEMP] = xs->sp - xs->fp; + + if (xs->addr.pc.offset >= code_buf_size) + script_error(s, "[VM] "__FILE__, __LINE__, "Program Counter gone astray"); +#endif + + opcode = GET_OP_BYTE(); /* Get opcode */ + + opnumber = opcode >> 1; + + for (temp = 0; formats[opnumber][temp]; temp++) /* formats comes from script.c */ + switch(formats[opnumber][temp]) { + + case Script_Byte: opparams[temp] = GET_OP_BYTE(); break; + case Script_SByte: opparams[temp] = GET_OP_SIGNED_BYTE(); break; + + case Script_Word: opparams[temp] = GET_OP_WORD(); break; + case Script_SWord: opparams[temp] = GET_OP_SIGNED_WORD(); break; + + case Script_Variable: + case Script_Property: + + case Script_Local: + case Script_Temp: + case Script_Global: + case Script_Param: + opparams[temp] = GET_OP_FLEX(); break; + + case Script_SVariable: + case Script_SRelative: + opparams[temp] = GET_OP_SIGNED_FLEX(); break; + + case Script_Offset: + opparams[temp] = GET_OP_FLEX(); break; + + case Script_None: + case Script_End: + break; + + case Script_Invalid: + default: + sciprintf("opcode %02x: Invalid!", opcode); + script_debug_flag = script_error_flag = 1; + + } + + + switch (opnumber) { + + case 0x00: /* bnot */ + s->r_acc = ACC_ARITHMETIC_L (0xffff ^ /*acc*/); + break; + + case 0x01: /* add */ + r_temp = POP32(); + if (r_temp.segment || s->r_acc.segment) { + reg_t r_ptr; + int offset; + /* Pointer arithmetics! */ + if (s->r_acc.segment) { + if (r_temp.segment) { + sciprintf("Error: Attempt to add two pointers, stack="PREG" and acc="PREG"!\n", + PRINT_REG(r_temp), PRINT_REG(s->r_acc)); + script_debug_flag = script_error_flag = 1; + offset = 0; + } else { + r_ptr = s->r_acc; + offset = r_temp.offset; + } + } else { + r_ptr = r_temp; + offset = s->r_acc.offset; + } + + s->r_acc = pointer_add(s, r_ptr, offset); + + } else + s->r_acc = make_reg(0, r_temp.offset + s->r_acc.offset); + break; + + case 0x02: /* sub */ + r_temp = POP32(); + if (r_temp.segment || s->r_acc.segment) { + reg_t r_ptr; + int offset; + /* Pointer arithmetics! */ + if (s->r_acc.segment) { + if (r_temp.segment) { + sciprintf("Error: Attempt to subtract two pointers, stack="PREG" and acc="PREG"!\n", + PRINT_REG(r_temp), PRINT_REG(s->r_acc)); + script_debug_flag = script_error_flag = 1; + offset = 0; + } else { + r_ptr = s->r_acc; + offset = r_temp.offset; + } + } else { + r_ptr = r_temp; + offset = s->r_acc.offset; + } + + s->r_acc = pointer_add(s, r_ptr, -offset); + + } else + s->r_acc = make_reg(0, r_temp.offset - s->r_acc.offset); + break; + + case 0x03: /* mul */ + s->r_acc = ACC_ARITHMETIC_L (((gint16)POP()) * (gint16)/*acc*/); + break; + + case 0x04: /* div */ + ACC_AUX_LOAD(); + aux_acc = aux_acc != 0 ? ((gint16)POP()) / aux_acc : 0; + ACC_AUX_STORE(); + break; + + case 0x05: /* mod */ + ACC_AUX_LOAD(); + aux_acc = aux_acc != 0 ? ((gint16)POP()) % aux_acc : 0; + ACC_AUX_STORE(); + break; + + case 0x06: /* shr */ + s->r_acc = ACC_ARITHMETIC_L(((guint16) POP()) >> /*acc*/); + break; + + case 0x07: /* shl */ + s->r_acc = ACC_ARITHMETIC_L(((guint16) POP()) << /*acc*/); + break; + + case 0x08: /* xor */ + s->r_acc = ACC_ARITHMETIC_L(POP() ^ /*acc*/); + break; + + case 0x09: /* and */ + s->r_acc = ACC_ARITHMETIC_L(POP() & /*acc*/); + break; + + case 0x0a: /* or */ + s->r_acc = ACC_ARITHMETIC_L(POP() | /*acc*/); + break; + + case 0x0b: /* neg */ + s->r_acc = ACC_ARITHMETIC_L(-/*acc*/); + break; + + case 0x0c: /* not */ + s->r_acc = make_reg(0, !(s->r_acc.offset || s->r_acc.segment)); + /* Must allow pointers to be negated, as this is used for + ** checking whether objects exist */ + break; + + case 0x0d: /* eq? */ + s->r_prev = s->r_acc; + r_temp = POP32(); + s->r_acc = make_reg(0, REG_EQ(r_temp, s->r_acc)); + /* Explicitly allow pointers to be compared */ + break; + + case 0x0e: /* ne? */ + s->r_prev = s->r_acc; + r_temp = POP32(); + s->r_acc = make_reg(0, !REG_EQ(r_temp, s->r_acc)); + /* Explicitly allow pointers to be compared */ + break; + + case 0x0f: /* gt? */ + s->r_prev = s->r_acc; + s->r_acc = ACC_ARITHMETIC_L((gint16)POP() > (gint16)/*acc*/); + break; + + case 0x10: /* ge? */ + s->r_prev = s->r_acc; + s->r_acc = ACC_ARITHMETIC_L((gint16)POP() >= (gint16)/*acc*/); + break; + + case 0x11: /* lt? */ + s->r_prev = s->r_acc; + s->r_acc = ACC_ARITHMETIC_L((gint16)POP() < (gint16)/*acc*/); + break; + + case 0x12: /* le? */ + s->r_prev = s->r_acc; + s->r_acc = ACC_ARITHMETIC_L((gint16)POP() <= (gint16)/*acc*/); + break; + + case 0x13: /* ugt? */ + s->r_prev = s->r_acc; + r_temp = POP32(); + s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset > s->r_acc.offset); + break; + + case 0x14: /* uge? */ + s->r_prev = s->r_acc; + r_temp = POP32(); + s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset >= s->r_acc.offset); + break; + + case 0x15: /* ult? */ + s->r_prev = s->r_acc; + r_temp = POP32(); + s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset < s->r_acc.offset); + break; + + case 0x16: /* ule? */ + s->r_prev = s->r_acc; + r_temp = POP32(); + s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset <= s->r_acc.offset); + break; + + case 0x17: /* bt */ + if (s->r_acc.offset || s->r_acc.segment) + xs->addr.pc.offset += opparams[0]; + break; + + case 0x18: /* bnt */ + if (!(s->r_acc.offset || s->r_acc.segment)) + xs->addr.pc.offset += opparams[0]; + break; + + case 0x19: /* jmp */ + xs->addr.pc.offset += opparams[0]; + break; + + case 0x1a: /* ldi */ + s->r_acc = make_reg(0, opparams[0]); + break; + + case 0x1b: /* push */ + PUSH32(s->r_acc); + break; + + case 0x1c: /* pushi */ + PUSH(opparams[0]); + break; + + case 0x1d: /* toss */ + xs->sp--; + break; + + case 0x1e: /* dup */ + r_temp = xs->sp[-1]; + PUSH32(r_temp); + break; + + case 0x1f: { /* link */ + int i; + for (i = 0; i < opparams[0]; i++) + xs->sp[i] = NULL_REG; + xs->sp += opparams[0]; + break; + } + + case 0x20: { /* call */ + int argc = (opparams[1] >> 1) /* Given as offset, but we need count */ + + 1 + restadjust; + stack_ptr_t call_base = xs->sp - argc; + + xs->sp[1].offset += restadjust; + xs_new = add_exec_stack_entry(s, make_reg(xs->addr.pc.segment, + xs->addr.pc.offset + + opparams[0]), + xs->sp, xs->objp, + (validate_arithmetic(*call_base)) + + restadjust, + call_base, NULL_SELECTOR, xs->objp, + s->execution_stack_pos, xs->local_segment); + restadjust = 0; /* Used up the &rest adjustment */ + xs->sp = call_base; + + s->execution_stack_pos_changed = 1; + break; + } + + case 0x21: /* callk */ + gc_countdown(s); + + xs->sp -= (opparams[1] >> 1)+1; + if (s->version >= SCI_VERSION_FTU_NEW_SCRIPT_HEADER) { + xs->sp -= restadjust; + s->r_amp_rest = 0; /* We just used up the restadjust, remember? */ + } + + if (opparams[0] >= s->kfunct_nr) { + + sciprintf("Invalid kernel function 0x%x requested\n", opparams[0]); + script_debug_flag = script_error_flag = 1; + + } else { + int argc = ASSERT_ARITHMETIC(xs->sp[0]); + + if (s->version >= SCI_VERSION_FTU_NEW_SCRIPT_HEADER) + argc += restadjust; + + if (s->kfunct_table[opparams[0]].signature + && !kernel_matches_signature(s, + s->kfunct_table[opparams[0]] + .signature, + argc, xs->sp + 1)) { + sciprintf("[VM] Invalid arguments to kernel call %x\n", + opparams[0]); + script_debug_flag = script_error_flag = 1; + } else { + s->r_acc = s->kfunct_table[opparams[0]] + .fun(s, opparams[0], argc, xs->sp + 1); + } + /* Call kernel function */ + + /* Calculate xs again: The kernel function might + ** have spawned a new VM */ + + xs_new = s->execution_stack + s->execution_stack_pos; + s->execution_stack_pos_changed = 1; + + if (s->version>=SCI_VERSION_FTU_NEW_SCRIPT_HEADER) + restadjust = s->r_amp_rest; + + } + break; + + case 0x22: /* callb */ + temp = ((opparams[1] >> 1) + restadjust + 1); + s_temp = xs->sp; + xs->sp -= temp; + + + xs->sp[0].offset += restadjust; + xs_new = execute_method(s, 0, opparams[0], s_temp, xs->objp, + xs->sp[0].offset, xs->sp); + restadjust = 0; /* Used up the &rest adjustment */ + if (xs_new) /* in case of error, keep old stack */ + s->execution_stack_pos_changed = 1; + break; + + case 0x23: /* calle */ + temp = ((opparams[2] >> 1) + restadjust + 1); + s_temp = xs->sp; + xs->sp -= temp; + + xs->sp[0].offset += restadjust; + xs_new = execute_method(s, opparams[0], opparams[1], s_temp, xs->objp, + xs->sp[0].offset, xs->sp); + restadjust = 0; /* Used up the &rest adjustment */ + + if (xs_new) /* in case of error, keep old stack */ + s->execution_stack_pos_changed = 1; + break; + + case 0x24: /* ret */ + do { + stack_ptr_t old_sp = xs->sp; + stack_ptr_t old_fp = xs->fp; + exec_stack_t *old_xs = s->execution_stack + s->execution_stack_pos; + + if (s->execution_stack_pos == s->execution_stack_base) { /* Have we reached the base? */ + s->execution_stack_base = old_execution_stack_base; /* Restore stack base */ + + --(s->execution_stack_pos); + + s->execution_stack_pos_changed = 1; + s->r_amp_rest = restadjust; /* Update &rest */ + return; /* "Hard" return */ + } + + if (old_xs->type == EXEC_STACK_TYPE_VARSELECTOR) { + /* varselector access? */ + if (old_xs->argc) /* write? */ + *(old_xs->addr.varp) = old_xs->variables_argp[1]; + else /* No, read */ + s->r_acc = *(old_xs->addr.varp); + } + + /* Not reached the base, so let's do a soft return */ + --(s->execution_stack_pos); + xs = old_xs - 1; + s->execution_stack_pos_changed = 1; + xs = s->execution_stack + s->execution_stack_pos; + + if (xs->sp == CALL_SP_CARRY /* Used in sends to 'carry' the stack pointer */ + || xs->type != EXEC_STACK_TYPE_CALL) { + xs->sp = old_sp; + xs->fp = old_fp; + } + + } while (xs->type == EXEC_STACK_TYPE_VARSELECTOR); + /* Iterate over all varselector accesses */ + s->execution_stack_pos_changed = 1; + xs_new = xs; + + break; + + case 0x25: /* send */ + s_temp = xs->sp; + xs->sp -= ((opparams[0] >> 1) + restadjust); /* Adjust stack */ + + xs->sp[1].offset += restadjust; + xs_new = send_selector(s, s->r_acc, s->r_acc, s_temp, + (int)(opparams[0]>>1) + (word)restadjust, + xs->sp); + + if (xs_new && xs_new != xs) + s->execution_stack_pos_changed = 1; + + restadjust = 0; + + break; + + case 0x28: /* class */ + s->r_acc = get_class_address(s, (unsigned) opparams[0], SCRIPT_GET_LOCK, xs->addr.pc); + break; + + case 0x2a: /* self */ + s_temp = xs->sp; + xs->sp -= ((opparams[0] >> 1) + restadjust); /* Adjust stack */ + + xs->sp[1].offset += restadjust; + xs_new = send_selector(s, xs->objp, xs->objp, s_temp, + (int)(opparams[0]>>1) + (word)restadjust, + xs->sp); + + if (xs_new && xs_new != xs) + s->execution_stack_pos_changed = 1; + + restadjust = 0; + break; + + case 0x2b: /* super */ + r_temp = get_class_address(s, opparams[0], SCRIPT_GET_LOAD, xs->addr.pc); + + if (!r_temp.segment) + CORE_ERROR("VM", "Invalid superclass in object"); + else { + s_temp = xs->sp; + xs->sp -= ((opparams[1] >> 1) + restadjust); /* Adjust stack */ + + xs->sp[1].offset += restadjust; + xs_new = send_selector(s, r_temp, xs->objp, s_temp, + (int)(opparams[1]>>1) + (word)restadjust, + xs->sp); + + if (xs_new && xs_new != xs) + s->execution_stack_pos_changed = 1; + + restadjust = 0; + } + + break; + + case 0x2c: /* &rest */ + temp = (guint16) opparams[0]; /* First argument */ + restadjust = xs->argc - temp + 1; /* +1 because temp counts the paramcount while argc doesn't */ + if (restadjust < 0) + restadjust = 0; + + for (; temp <= xs->argc; temp++) + PUSH32(xs->variables_argp[temp]); + + break; + + case 0x2d: /* lea */ + temp = (guint16) opparams[0] >> 1; + var_number = temp & 0x03; /* Get variable type */ + + /* Get variable block offset */ + r_temp.segment = variables_seg[var_number]; + r_temp.offset = variables[var_number] - variables_base[var_number]; + + if (temp & 0x08) /* Add accumulator offset if requested */ + r_temp.offset += signed_validate_arithmetic(s->r_acc); + + r_temp.offset += opparams[1]; /* Add index */ + r_temp.offset *= sizeof(reg_t); + /* That's the immediate address now */ + s->r_acc = r_temp; + break; + + + case 0x2e: /* selfID */ + s->r_acc = xs->objp; + break; + + case 0x30: /* pprev */ + PUSH32(s->r_prev); + break; + + case 0x31: /* pToa */ + s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1)); + break; + + case 0x32: /* aTop */ + OBJ_PROPERTY(obj, (opparams[0] >> 1)) = s->r_acc; + break; + + case 0x33: /* pTos */ + PUSH32(OBJ_PROPERTY(obj, opparams[0] >> 1)); + break; + + case 0x34: /* sTop */ + OBJ_PROPERTY(obj, (opparams[0] >> 1)) = POP32(); + break; + + case 0x35: /* ipToa */ + s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1)); + s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1)) = + ACC_ARITHMETIC_L( 1 + /*acc*/); + break; + + case 0x36: /* dpToa */ + s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1)); + s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1)) = + ACC_ARITHMETIC_L(-1 + /*acc*/); + break; + + case 0x37: /* ipTos */ + ASSERT_ARITHMETIC(OBJ_PROPERTY(obj, (opparams[0] >> 1))); + temp = ++OBJ_PROPERTY(obj, (opparams[0] >> 1)).offset; + PUSH(temp); + break; + + case 0x38: /* dpTos */ + ASSERT_ARITHMETIC(OBJ_PROPERTY(obj, (opparams[0] >> 1))); + temp = --OBJ_PROPERTY(obj, (opparams[0] >> 1)).offset; + PUSH(temp); + break; + + + case 0x39: /* lofsa */ + s->r_acc.segment = xs->addr.pc.segment; + + if (s->version >= SCI_VERSION(1,001,000)) + s->r_acc.offset = opparams[0]+local_script->script_size; + else + if (s->version >= SCI_VERSION_FTU_LOFS_ABSOLUTE) + s->r_acc.offset = opparams[0]; + else + s->r_acc.offset = xs->addr.pc.offset + opparams[0]; +#ifndef DISABLE_VALIDATIONS + if (s->r_acc.offset >= code_buf_size) { + sciprintf("VM: lofsa operation overflowed: "PREG" beyond end" + " of script (at %04x)\n", PRINT_REG(s->r_acc), + code_buf_size); + script_error_flag = script_debug_flag = 1; + } +#endif + break; + + + case 0x3a: /* lofss */ + r_temp.segment = xs->addr.pc.segment; + + if (s->version >= SCI_VERSION_FTU_LOFS_ABSOLUTE) + r_temp.offset = opparams[0]; else + r_temp.offset = xs->addr.pc.offset + opparams[0]; +#ifndef DISABLE_VALIDATIONS + if (r_temp.offset >= code_buf_size) { + sciprintf("VM: lofss operation overflowed: "PREG" beyond end" + " of script (at %04x)\n", PRINT_REG(r_temp), + code_buf_size); + script_error_flag = script_debug_flag = 1; + } +#endif + PUSH32(r_temp); + break; + + case 0x3b: /* push0 */ + PUSH(0); + break; + + case 0x3c: /* push1 */ + PUSH(1); + break; + + case 0x3d: /* push2 */ + PUSH(2); + break; + + case 0x3e: /* pushSelf */ + PUSH32(xs->objp); + break; + + case 0x40: /* lag */ + case 0x41: /* lal */ + case 0x42: /* lat */ + case 0x43: /* lap */ + var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */ + var_number = opparams[0]; + s->r_acc = READ_VAR(var_type, var_number, s->r_acc); + break; + + case 0x44: /* lsg */ + case 0x45: /* lsl */ + case 0x46: /* lst */ + case 0x47: /* lsp */ + var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */ + var_number = opparams[0]; + PUSH32(READ_VAR(var_type, var_number, s->r_acc)); + break; + + case 0x48: /* lagi */ + case 0x49: /* lali */ + case 0x4a: /* lati */ + case 0x4b: /* lapi */ + var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */ + var_number = opparams[0] + signed_validate_arithmetic(s->r_acc); + s->r_acc = READ_VAR(var_type, var_number, s->r_acc); + break; + + case 0x4c: /* lsgi */ + case 0x4d: /* lsli */ + case 0x4e: /* lsti */ + case 0x4f: /* lspi */ + var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */ + var_number = opparams[0] + signed_validate_arithmetic(s->r_acc); + PUSH32(READ_VAR(var_type, var_number, s->r_acc)); + break; + + case 0x50: /* sag */ + case 0x51: /* sal */ + case 0x52: /* sat */ + case 0x53: /* sap */ + var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */ + var_number = opparams[0]; + WRITE_VAR(var_type, var_number, s->r_acc); + break; + + case 0x54: /* ssg */ + case 0x55: /* ssl */ + case 0x56: /* sst */ + case 0x57: /* ssp */ + var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */ + var_number = opparams[0]; + WRITE_VAR(var_type, var_number, POP32()); + break; + + case 0x58: /* sagi */ + case 0x59: /* sali */ + case 0x5a: /* sati */ + case 0x5b: /* sapi */ + /* Special semantics because it wouldn't really make a whole lot + ** of sense otherwise, with acc being used for two things + ** simultaneously... */ + var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */ + var_number = opparams[0] + signed_validate_arithmetic(s->r_acc); + WRITE_VAR(var_type, var_number, s->r_acc = POP32()); + break; + + case 0x5c: /* ssgi */ + case 0x5d: /* ssli */ + case 0x5e: /* ssti */ + case 0x5f: /* sspi */ + var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */ + var_number = opparams[0] + signed_validate_arithmetic(s->r_acc); + WRITE_VAR(var_type, var_number, POP32()); + break; + + case 0x60: /* +ag */ + case 0x61: /* +al */ + case 0x62: /* +at */ + case 0x63: /* +ap */ + var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */ + var_number = opparams[0]; + s->r_acc = make_reg(0, + 1 + validate_arithmetic(READ_VAR(var_type, + var_number, + s->r_acc))); + WRITE_VAR(var_type, var_number, s->r_acc); + break; + + case 0x64: /* +sg */ + case 0x65: /* +sl */ + case 0x66: /* +st */ + case 0x67: /* +sp */ + var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */ + var_number = opparams[0]; + r_temp = make_reg(0, + 1 + validate_arithmetic(READ_VAR(var_type, + var_number, + s->r_acc))); + PUSH32(r_temp); + WRITE_VAR(var_type, var_number, r_temp); + break; + + case 0x68: /* +agi */ + case 0x69: /* +ali */ + case 0x6a: /* +ati */ + case 0x6b: /* +api */ + var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */ + var_number = opparams[0] + signed_validate_arithmetic(s->r_acc); + s->r_acc = make_reg(0, + 1 + validate_arithmetic(READ_VAR(var_type, + var_number, + s->r_acc))); + WRITE_VAR(var_type, var_number, s->r_acc); + break; + + case 0x6c: /* +sgi */ + case 0x6d: /* +sli */ + case 0x6e: /* +sti */ + case 0x6f: /* +spi */ + var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */ + var_number = opparams[0] + signed_validate_arithmetic(s->r_acc); + r_temp = make_reg(0, + 1 + validate_arithmetic(READ_VAR(var_type, + var_number, + s->r_acc))); + PUSH32(r_temp); + WRITE_VAR(var_type, var_number, r_temp); + break; + + case 0x70: /* -ag */ + case 0x71: /* -al */ + case 0x72: /* -at */ + case 0x73: /* -ap */ + var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */ + var_number = opparams[0]; + s->r_acc = make_reg(0, + -1 + validate_arithmetic(READ_VAR(var_type, + var_number, s->r_acc))); + WRITE_VAR(var_type, var_number, s->r_acc); + break; + + case 0x74: /* -sg */ + case 0x75: /* -sl */ + case 0x76: /* -st */ + case 0x77: /* -sp */ + var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */ + var_number = opparams[0]; + r_temp = make_reg(0, + -1 + validate_arithmetic(READ_VAR(var_type, + var_number, s->r_acc))); + PUSH32(r_temp); + WRITE_VAR(var_type, var_number, r_temp); + break; + + case 0x78: /* -agi */ + case 0x79: /* -ali */ + case 0x7a: /* -ati */ + case 0x7b: /* -api */ + var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */ + var_number = opparams[0] + signed_validate_arithmetic(s->r_acc); + s->r_acc = make_reg(0, + -1 + validate_arithmetic(READ_VAR(var_type, + var_number, + s->r_acc))); + WRITE_VAR(var_type, var_number, s->r_acc); + break; + + case 0x7c: /* -sgi */ + case 0x7d: /* -sli */ + case 0x7e: /* -sti */ + case 0x7f: /* -spi */ + var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */ + var_number = opparams[0] + signed_validate_arithmetic(s->r_acc); + r_temp = make_reg(0, + -1 + validate_arithmetic(READ_VAR(var_type, + var_number, + s->r_acc))); + PUSH32(r_temp); + WRITE_VAR(var_type, var_number, r_temp); + break; + + default: + script_error(s, __FILE__, __LINE__, "Illegal opcode"); + + } /* switch(opcode >> 1) */ + + + if (s->execution_stack_pos_changed) /* Force initialization */ + xs = xs_new; + +#ifndef DISABLE_VALIDATIONS + if (xs != s->execution_stack + s->execution_stack_pos) { + sciprintf("Error: xs is stale (%d vs %d); last command was %02x\n", + xs-s->execution_stack, s->execution_stack_pos, opnumber); + } +#endif + + if (script_error_flag) { + _debug_step_running = 0; /* Stop multiple execution */ + _debug_seeking = 0; /* Stop special seeks */ + xs->addr.pc.offset = old_pc_offset; + xs->sp = old_sp; + } else + ++script_step_counter; + } +} + + + +static inline int +_obj_locate_varselector(state_t *s, object_t *obj, selector_t slc) +{ /* Determines if obj explicitly defines slc as a varselector */ + /* Returns -1 if not found */ + + if (s->version < SCI_VERSION(1,001,000)) + { + int varnum = obj->variable_names_nr; + int selector_name_offset = varnum * 2 + SCRIPT_SELECTOR_OFFSET; + int i; + byte *buf = obj->base_obj + selector_name_offset; + + obj->base_vars = (guint16 *) buf; + + for (i = 0; i < varnum; i++) + if (getUInt16(buf + (i << 1)) == slc) /* Found it? */ + return i; /* report success */ + + return -1; /* Failed */ + } + else + { + byte *buf = (byte *) obj->base_vars; + int i; + int varnum = obj->variables[1].offset; + + if (!(obj->variables[SCRIPT_INFO_SELECTOR].offset & SCRIPT_INFO_CLASS)) + buf = ((byte *) obj_get(s, obj->variables[SCRIPT_SUPERCLASS_SELECTOR])->base_vars); + + for (i = 0; i < varnum; i++) + if (getUInt16(buf + (i << 1)) == slc) /* Found it? */ + return i; /* report success */ + + return -1; /* Failed */ + } +} + + +static inline int +_class_locate_funcselector(state_t *s, object_t *obj, selector_t slc) +{ /* Determines if obj is a class and explicitly defines slc as a funcselector */ + /* Does NOT say anything about obj's superclasses, i.e. failure may be + ** returned even if one of the superclasses defines the funcselector. */ + int funcnum = obj->methods_nr; + int i; + + for (i = 0; i < funcnum; i++) + if (VM_OBJECT_GET_FUNCSELECTOR(obj, i) == slc) /* Found it? */ + return i; /* report success */ + + return -1; /* Failed */ +} + + +static inline int +_lookup_selector_function(state_t *s, int seg_id, object_t *obj, selector_t selector_id, reg_t *fptr) +{ + int index; + + /* "recursive" lookup */ + + while (obj) { + index = _class_locate_funcselector(s, obj, selector_id); + + if (index >= 0) { + if (fptr) + { + if (s->version < SCI_VERSION(1,001,000)) + *fptr = make_reg(obj->pos.segment, + getUInt16((byte *) + (obj->base_method + index + + obj->methods_nr + 1))); + else + *fptr = make_reg(obj->pos.segment, + getUInt16((byte *) + (obj->base_method + index * 2 + 2))); + } + + return SELECTOR_METHOD; + } else { + seg_id = obj->variables[SCRIPT_SUPERCLASS_SELECTOR].segment; + obj = obj_get(s, obj->variables[SCRIPT_SUPERCLASS_SELECTOR]); + } + } + + return SELECTOR_NONE; +} + +int +lookup_selector(state_t *s, reg_t obj_location, selector_t selector_id, reg_t **vptr, reg_t *fptr) +{ + object_t *obj = obj_get(s, obj_location); + object_t *species; + int index; + + /* Early SCI versions used the LSB in the selector ID as a read/write + ** toggle, meaning that we must remove it for selector lookup. */ + if (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER) + selector_id &= ~1; + + if (!obj) { + CORE_ERROR("SLC-LU", "Attempt to send to non-object or invalid script"); + sciprintf("Address was "PREG"\n", PRINT_REG(obj_location)); + return SELECTOR_NONE; + } + + if (IS_CLASS(obj)) + species = obj; + else + species = obj_get(s, obj->variables[SCRIPT_SPECIES_SELECTOR]); + + + if (!obj) { + CORE_ERROR("SLC-LU", "Error while looking up Species class"); + sciprintf("Original address was "PREG"\n", PRINT_REG(obj_location)); + sciprintf("Species address was "PREG"\n", PRINT_REG(obj->variables[SCRIPT_SPECIES_SELECTOR])); + return SELECTOR_NONE; + } + + index = _obj_locate_varselector(s, obj, selector_id); + + if (index >= 0) { + /* Found it as a variable */ + if (vptr) + *vptr = obj->variables + index; + return SELECTOR_VARIABLE; + } return + _lookup_selector_function(s, obj_location.segment, obj, selector_id, fptr); + +} + + +/* Detects SCI versions by their different script header */ +void script_detect_versions(state_t *s) +{ + int c; + resource_t *script = {0}; + + + if (scir_find_resource(s->resmgr, sci_heap, 0, 0)) + { + version_require_later_than(s, SCI_VERSION(1,001,000)); + return; + } + + for (c = 0; c < 1000; c++) { + if ((script = scir_find_resource(s->resmgr, sci_script, c, 0))) { + + int id = getInt16(script->data); + + if (id > 15) { + version_require_earlier_than(s, SCI_VERSION_FTU_NEW_SCRIPT_HEADER); + return; + } + } + } +} + + +seg_id_t +script_get_segment(state_t *s, int script_nr, int load) +{ + seg_id_t segment; + + if ((load & SCRIPT_GET_LOAD) == SCRIPT_GET_LOAD) + script_instantiate(s, script_nr); + + segment = sm_seg_get(&s->seg_manager, script_nr); + + if (segment > 0) { + if ((load & SCRIPT_GET_LOCK) == SCRIPT_GET_LOCK) + sm_increment_lockers(&s->seg_manager, segment, SEG_ID); + + return segment; + } else + return 0; +} + +reg_t +script_lookup_export(state_t *s, int script_nr, int export_index) +{ + seg_id_t seg = script_get_segment(s, script_nr, SCRIPT_GET_DONT_LOAD); + mem_obj_t *memobj; + script_t *script = NULL; + +#ifndef DISABLE_VALIDATIONS + if (!seg) { + CORE_ERROR("EXPORTS", "Script invalid or not loaded"); + sciprintf("Script was script.03d (0x%x)\n", + script_nr, script_nr); + return NULL_REG; + } +#endif + + memobj = GET_SEGMENT(s->seg_manager, seg, MEM_OBJ_SCRIPT); + + if (memobj) + script = &(memobj->data.script); + +#ifndef DISABLE_VALIDATIONS + if (script + && export_index < script->exports_nr + && export_index >= 0) +#endif + return make_reg(seg, getUInt16((byte *)(script->export_table + export_index))); +#ifndef DISABLE_VALIDATIONS + else { + CORE_ERROR("EXPORTS", "Export invalid or script missing "); + if (!script) + sciprintf("(script.%03d missing)\n", script_nr); + else + sciprintf("(script.%03d: Sought export %d/%d)\n", + script_nr, export_index, script->exports_nr); + return NULL_REG; + } +#endif +} + +#define INST_LOOKUP_CLASS(id) ((id == 0xffff)? NULL_REG : get_class_address(s, id, SCRIPT_GET_LOCK, reg)) + +int sm_script_marked_deleted(seg_manager_t* self, int script_nr); +int sm_initialise_script(mem_obj_t *mem, struct _state *s, int script_nr); + +int +script_instantiate_common(state_t *s, int script_nr, resource_t **script, resource_t **heap, int *was_new) +{ + int seg; + int seg_id; + int marked_for_deletion; + mem_obj_t *mem; + reg_t reg; + + *was_new = 1; + + *script = scir_find_resource(s->resmgr, sci_script, script_nr, 0); + if (s->version >= SCI_VERSION(1,001,000)) + *heap = scir_find_resource(s->resmgr, sci_heap, script_nr, 0); + + if (!*script || (s->version >= SCI_VERSION(1,001,000) && !heap)) { + sciprintf("Script 0x%x requested but not found\n", script_nr); + /* script_debug_flag = script_error_flag = 1; */ + if (s->version >= SCI_VERSION(1,001,000)) + { + if (*heap) + sciprintf("Inconsistency: heap resource WAS found\n"); + else if (*script) + sciprintf("Inconsistency: script resource WAS found\n"); + } + return 0; + } + + if (NULL == s) { + sciprintf("vm.c: script_instantiate(): NULL passed for \"s\"\n"); + return 0; + } + + seg = sm_seg_get ( &s->seg_manager, script_nr ); + if (sm_script_is_loaded (&s->seg_manager, script_nr, SCRIPT_ID)) { + marked_for_deletion = sm_script_marked_deleted(&s->seg_manager, script_nr); + + if (!marked_for_deletion) { + sm_increment_lockers( &s->seg_manager, seg, SEG_ID ); + return seg; + } + else + { + seg_id = seg; + mem = s->seg_manager.heap[seg]; + sm_free_script(mem); + } + } + else if (!(mem = sm_allocate_script( &s->seg_manager, s, script_nr, &seg_id ))) { /* ALL YOUR SCRIPT BASE ARE BELONG TO US */ + sciprintf("Not enough heap space for script size 0x%x of script 0x%x," + " should this happen?`\n", + (*script)->size, script_nr); + script_debug_flag = script_error_flag = 1; + return 0; + } + + sm_initialise_script(mem, s, script_nr); + + reg.segment = seg_id; + reg.offset = 0; + + /* Set heap position (beyond the size word) */ + sm_set_lockers( &s->seg_manager, 1, reg.segment, SEG_ID ); + sm_set_export_table_offset( &s->seg_manager, 0, reg.segment, SEG_ID ); + sm_set_synonyms_offset( &s->seg_manager, 0, reg.segment, SEG_ID ); + sm_set_synonyms_nr( &s->seg_manager, 0, reg.segment, SEG_ID ); + + *was_new = 0; + + return seg_id; +} + +int +script_instantiate_sci0(state_t *s, int script_nr) +{ + int objtype; + unsigned int objlength; + reg_t reg, reg_tmp; + int seg_id; + int relocation = -1; + int magic_pos_adder; /* Usually 0; 2 for older SCI versions */ + resource_t *script; + int was_new; + + seg_id = script_instantiate_common(s, script_nr, &script, NULL, &was_new); + + if (was_new) return seg_id; + + reg.segment = seg_id; + reg.offset = 0; + + if (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER) { + /* + int locals_size = getUInt16(script->data)*2; + int locals = (locals_size)? script->size : 0; + */ + int locals_nr = getUInt16(script->data); + + /* Old script block */ + /* There won't be a localvar block in this case */ + /* Instead, the script starts with a 16 bit int specifying the + ** number of locals we need; these are then allocated and zeroed. */ + + sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, reg.segment, SEG_ID); + magic_pos_adder = 2; /* Step over the funny prefix */ + + if (locals_nr) + sm_script_initialise_locals_zero( &s->seg_manager, + reg.segment, locals_nr); + + } else { + sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, reg.segment, SEG_ID); + magic_pos_adder = 0; + } + + /* Now do a first pass through the script objects to find the + ** export table and local variable block + */ + + objlength = 0; + reg_tmp = reg; + reg.offset = magic_pos_adder; + + do { + reg_t data_base; + reg_t addr; + reg.offset += objlength; /* Step over the last checked object */ + objtype = SEG_GET_HEAP(s, reg); + if( !objtype ) break; + + objlength = SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + 2)); + + data_base = reg; + data_base.offset += 4; + + addr = data_base; + + switch( objtype ) { + case sci_obj_exports: { + sm_set_export_table_offset( &s->seg_manager, data_base.offset, + reg.segment, SEG_ID ); + } + break; + + case sci_obj_synonyms: + sm_set_synonyms_offset( &s->seg_manager, addr.offset, reg.segment, SEG_ID ); /* +4 is to step over the header */ + sm_set_synonyms_nr( &s->seg_manager, (objlength) / 4, reg.segment, SEG_ID ); + break; + + case sci_obj_localvars: + sm_script_initialise_locals( &s->seg_manager, data_base); + break; + + case sci_obj_class: { + int classpos = addr.offset - SCRIPT_OBJECT_MAGIC_OFFSET; + int species; + reg_tmp.offset = addr.offset - SCRIPT_OBJECT_MAGIC_OFFSET; + species = OBJ_SPECIES(s, reg_tmp); + if (species < 0 || species >= s->classtable_size) { + sciprintf("Invalid species %d(0x%x) not in interval " + "[0,%d) while instantiating script %d\n", + species, species, s->classtable_size, + script_nr); + script_debug_flag = script_error_flag = 1; + return 1; + } + + s->classtable[species].script = script_nr; + s->classtable[species].reg = addr; + s->classtable[species].reg.offset = classpos; + /* Set technical class position-- into the block allocated for it */ + + } + break; + + default: + break; + } + } while (objtype != 0); + /* And now a second pass to adjust objects and class pointers, and the general pointers */ + + objlength = 0; + reg.offset = magic_pos_adder; /* Reset counter */ + + do { + reg_t addr; + reg.offset += objlength; /* Step over the last checked object */ + objtype = SEG_GET_HEAP(s, reg); + if( !objtype ) break; + objlength = SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + 2)); + reg.offset += 4; /* Step over header */ + + addr = reg; + + switch (objtype) { + case sci_obj_code: + sm_script_add_code_block(&s->seg_manager, addr); + break; + case sci_obj_object: + case sci_obj_class: + { /* object or class? */ + object_t *obj = sm_script_obj_init(&s->seg_manager, s, addr); + object_t *base_obj; + + /* Instantiate the superclass, if neccessary */ + obj->variables[SCRIPT_SPECIES_SELECTOR] = + INST_LOOKUP_CLASS(obj->variables[SCRIPT_SPECIES_SELECTOR].offset); + + base_obj = obj_get(s, obj->variables[SCRIPT_SPECIES_SELECTOR]); + obj->variable_names_nr = base_obj->variables_nr; + obj->base_obj = base_obj->base_obj; + /* Copy base from species class, as we need its selector IDs */ + + obj->variables[SCRIPT_SUPERCLASS_SELECTOR] = + INST_LOOKUP_CLASS(obj->variables[SCRIPT_SUPERCLASS_SELECTOR].offset); + + } /* if object or class */ + break; + case sci_obj_pointers: /* A relocation table */ + relocation = addr.offset; + break; + + default: + break; + } + + reg.offset -= 4; /* Step back on header */ + + } while ((objtype != 0) && (((unsigned)reg.offset) < script->size - 2)); + + if (relocation >= 0) + sm_script_relocate(&s->seg_manager, make_reg(reg.segment, relocation)); + + sm_script_free_unused_objects(&s->seg_manager, reg.segment); + + return reg.segment; /* instantiation successful */ +} + +void +sm_script_relocate_exports_sci11(seg_manager_t *self, int seg); +void +sm_script_initialise_objects_sci11(seg_manager_t *self, state_t *s, int seg); +void +sm_heap_relocate(seg_manager_t *self, state_t *s, reg_t block); + +int +script_instantiate_sci11(state_t *s, int script_nr) +{ + resource_t *script, *heap; + int seg_id; + int heap_start; + reg_t reg; + int was_new; + + seg_id = script_instantiate_common(s, script_nr, &script, &heap, &was_new); + + if (was_new) return seg_id; + + heap_start = script->size; + if (script->size & 2) + heap_start ++; + + sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, seg_id, SEG_ID); + sm_mcpy_in_out( &s->seg_manager, heap_start, heap->data, heap->size, seg_id, SEG_ID); + + if (getUInt16(script->data+6) > 0) + sm_set_export_table_offset(&s->seg_manager, 6, + seg_id, SEG_ID); + + reg.segment = seg_id; + reg.offset = heap_start+4; + sm_script_initialise_locals(&s->seg_manager, reg); + + sm_script_relocate_exports_sci11(&s->seg_manager, seg_id); + sm_script_initialise_objects_sci11(&s->seg_manager, s, seg_id); + + reg.offset = getUInt16(heap->data); + sm_heap_relocate(&s->seg_manager, s, reg); + + return seg_id; +} + +int +script_instantiate(state_t *s, int script_nr) +{ + if (s->version >= SCI_VERSION(1,001,000)) + return script_instantiate_sci11(s, script_nr); + else + return script_instantiate_sci0(s, script_nr); +} + +void sm_mark_script_deleted(seg_manager_t* self, int script_nr); + +void +script_uninstantiate_sci0(state_t *s, int script_nr, seg_id_t seg) +{ + reg_t reg = make_reg( seg, (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER)? 2 : 0 ); + int objtype, objlength; + + /* Make a pass over the object in order uninstantiate all superclasses */ + objlength = 0; + + do { + reg.offset += objlength; /* Step over the last checked object */ + + objtype = SEG_GET_HEAP(s, reg); + if( !objtype ) break; + objlength = SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + 2)); /* use SEG_UGET_HEAP ?? */ + + reg.offset += 4; /* Step over header */ + + if ((objtype == sci_obj_object) || (objtype == sci_obj_class)) { /* object or class? */ + int superclass; + + reg.offset -= SCRIPT_OBJECT_MAGIC_OFFSET; + + superclass = OBJ_SUPERCLASS(s, reg); /* Get superclass... */ + + if (superclass >= 0) { + int superclass_script = s->classtable[superclass].script; + + if (superclass_script == script_nr) { + if( sm_get_lockers( &s->seg_manager, reg.segment, SEG_ID) ) + sm_decrement_lockers( &s->seg_manager, reg.segment, SEG_ID); /* Decrease lockers if this is us ourselves */ + } else + script_uninstantiate(s, superclass_script); + /* Recurse to assure that the superclass lockers number gets decreased */ + } + + reg.offset += SCRIPT_OBJECT_MAGIC_OFFSET; + } /* if object or class */ + + reg.offset -= 4; /* Step back on header */ + + } while (objtype != 0); +} + +void +script_uninstantiate(state_t *s, int script_nr) +{ + reg_t reg = make_reg( 0, (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER)? 2 : 0 ); + int i; + + reg.segment = sm_seg_get( &s->seg_manager, script_nr); + + if (!sm_script_is_loaded (&s->seg_manager, script_nr, SCRIPT_ID) || reg.segment <= 0 ) { /* Is it already loaded? */ + /* sciprintf("Warning: unloading script 0x%x requested although not loaded\n", script_nr); */ + /* This is perfectly valid SCI behaviour */ + return; + } + + sm_decrement_lockers( &s->seg_manager, reg.segment, SEG_ID); /* One less locker */ + + if( sm_get_lockers( &s->seg_manager, reg.segment, SEG_ID) > 0 ) + return; + + /* Free all classtable references to this script */ + for (i = 0; i < s->classtable_size; i++) + if (s->classtable[i].reg.segment == reg.segment) + s->classtable[i].reg = NULL_REG; + + if (s->version < SCI_VERSION(1,001,000)) + script_uninstantiate_sci0(s, script_nr, reg.segment); + else + sciprintf("FIXME: Add proper script uninstantiation for SCI 1.1\n"); + + if( sm_get_lockers( &s->seg_manager, reg.segment, SEG_ID) ) + return; /* if xxx.lockers > 0 */ + + /* Otherwise unload it completely */ + /* Explanation: I'm starting to believe that this work is done by SCI itself. */ + sm_mark_script_deleted( &s->seg_manager, script_nr ); + + if (script_checkloads_flag) + sciprintf("Unloaded script 0x%x.\n", script_nr); + + return; +} + + +static void +_init_stack_base_with_selector(state_t *s, selector_t selector) +{ + s->stack_base[0] = make_reg(0, (word) selector); + s->stack_base[1] = NULL_REG; +} + +static state_t * +_game_run(state_t *s, int restoring) +{ + state_t *successor = NULL; + int game_is_finished = 0; + do { + s->execution_stack_pos_changed = 0; + run_vm(s, (successor || restoring)? 1 : 0); + if (s->restarting_flags & SCI_GAME_IS_RESTARTING_NOW) { /* Restart was requested? */ + + sci_free(s->execution_stack); + s->execution_stack = NULL; + s->execution_stack_pos = -1; + s->execution_stack_pos_changed = 0; + + game_exit(s); + script_free_engine(s); + script_init_engine(s, s->version); + game_init(s); + sfx_reset_player(); + _init_stack_base_with_selector(s, s->selector_map.play); + /* Call the play selector */ + + send_selector(s, s->game_obj, s->game_obj, + s->stack_base, 2, s->stack_base); + + script_abort_flag = 0; + s->restarting_flags = SCI_GAME_WAS_RESTARTED | + SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE; + + } + else { + successor = s->successor; + if (successor) { + game_exit(s); + script_free_vm_memory(s); + sci_free(s); + s = successor; + + if (!send_calls_allocated) + send_calls = (calls_struct_t*)sci_calloc(sizeof(calls_struct_t), + send_calls_allocated = 16); + + if (script_abort_flag == SCRIPT_ABORT_WITH_REPLAY) { + sciprintf("Restarting with replay()\n"); + s->execution_stack_pos = -1; /* Resatart with replay */ + + _init_stack_base_with_selector(s, s->selector_map.replay); + /* Call the replay selector */ + + send_selector(s, s->game_obj, s->game_obj, + s->stack_base, 2, + s->stack_base); + } + + script_abort_flag = 0; + + } else + game_is_finished = 1; + } + } while (!game_is_finished); + + return s; +} + +int objinfo(state_t *s, reg_t pos); + +int +game_run(state_t **_s) +{ + state_t *s = *_s; + + sciprintf(" Calling %s::play()\n", s->game_name); + _init_stack_base_with_selector(s, s->selector_map.play); /* Call the play selector */ + + + /* Now: Register the first element on the execution stack- */ + if (!send_selector(s, s->game_obj, s->game_obj, + s->stack_base, 2, + s->stack_base) || script_error_flag) { + objinfo(s, s->game_obj); + sciprintf("Failed to run the game! Aborting...\n"); + return 1; + } + /* and ENGAGE! */ + *_s = s = _game_run(s, 0); + + sciprintf(" Game::play() finished.\n"); + return 0; +} + +int +game_restore(state_t **_s, char *game_name) +{ + state_t *s; + int debug_state = _debugstate_valid; + + sciprintf("Restoring savegame '%s'...\n", game_name); + s = gamestate_restore(*_s, game_name); + + if (!s) { + sciprintf("Restoring gamestate '%s' failed.\n", game_name); + return 1; + } + _debugstate_valid = debug_state; + script_abort_flag = 0; + s->restarting_flags = 0; + + s->execution_stack_pos = -1; /* Resatart with replay */ + + _init_stack_base_with_selector(s, s->selector_map.replay); + /* Call the replay selector */ + + send_selector(s, s->game_obj, s->game_obj, + s->stack_base, 2, + s->stack_base); + + *_s = s = _game_run(s, 1); + + sciprintf(" Game::play() finished.\n"); + return 0; +} + + +object_t * +obj_get(state_t *s, reg_t offset) +{ + mem_obj_t *memobj = GET_OBJECT_SEGMENT(s->seg_manager, offset.segment); + object_t *obj = NULL; + int idx; + + if (memobj != NULL) { + if (memobj->type == MEM_OBJ_CLONES + && ENTRY_IS_VALID(&memobj->data.clones, offset.offset)) + obj = &(memobj->data.clones.table[offset.offset].entry); + else if (memobj->type == MEM_OBJ_SCRIPT) { + if (offset.offset <= memobj->data.script.buf_size + && offset.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET + && RAW_IS_OBJECT(memobj->data.script.buf + offset.offset)) { + idx = RAW_GET_CLASS_INDEX(&(memobj->data.script), offset); + if (idx >= 0 && idx < memobj->data.script.objects_nr) + obj = memobj->data.script.objects + idx; + } + } + } + + return obj; +} + +const char * +obj_get_name(struct _state *s, reg_t pos) +{ + object_t *obj = obj_get(s, pos); + + if (!obj) + return ""; + + return + (const char*)(obj->base + obj->variables[SCRIPT_NAME_SELECTOR].offset); +} + + +void +quit_vm() +{ + script_abort_flag = 1; /* Terminate VM */ + _debugstate_valid = 0; + _debug_seeking = 0; + _debug_step_running = 0; +} + + diff --git a/engines/sci/gfx/Makefile.am b/engines/sci/gfx/Makefile.am new file mode 100644 index 0000000000..599d04aba5 --- /dev/null +++ b/engines/sci/gfx/Makefile.am @@ -0,0 +1,20 @@ +SUBDIRS = resource drivers +INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@ +EXTRA_DIST = gfx_line.c gfx_crossblit.c gfx_pixmap_scale.c alpha_mvi_crossblit.c + +noinst_LIBRARIES = libscigraphics.a +libscigraphics_a_LIBADD = alpha_mvi_crossblit_32.o alpha_mvi_crossblit_32_P.o drivers/libscidrivers.a resource/libsciresources.a +EXTRA_libscigraphics_a_SOURCES = gfx_crossblit.c gfx_pixmap_scale.c +libscigraphics_a_SOURCES = font.c gfx_resource.c gfx_support.c gfx_tools.c operations.c resmgr.c sbtree.c widgets.c menubar.c sci_widgets.c antialias.c gfx_console.c gfx_res_options.c font-5x8.c font-6x10.c + +check_PROGRAMS = gfx_test +gfx_test_LDADD = libscigraphics.a drivers/libscidrivers.a @X_LIBS@ @ac_glx_libraries@ @ac_ggi_libraries@ @ac_readline@ @ac_curses_libraries@ @ac_png_libraries@ -lm @SDL_LIBS@ +gfx_test_SOURCES = gfx_test.c + +alpha_mvi_crossblit_32.o: + $(COMPILE) @ev6_cpu@ -I../.. -I../include -DHAVE_CONFIG_H -DFUNCT_NAME=alpha_mvi_crossblit_32 -c $(top_srcdir)/src/gfx/alpha_mvi_crossblit.c -o $(top_builddir)/src/gfx/alpha_mvi_crossblit_32.o + +alpha_mvi_crossblit_32_P.o: + $(COMPILE) @ev6_cpu@ -I../.. -I../include -DHAVE_CONFIG_H -DPRIORITY=1 -DFUNCT_NAME=alpha_mvi_crossblit_32_P -c $(top_srcdir)/src/gfx/alpha_mvi_crossblit.c -o $(top_builddir)/src/gfx/alpha_mvi_crossblit_32_P.o + + diff --git a/engines/sci/gfx/alpha_mvi_crossblit.c b/engines/sci/gfx/alpha_mvi_crossblit.c new file mode 100644 index 0000000000..6a1e238c2b --- /dev/null +++ b/engines/sci/gfx/alpha_mvi_crossblit.c @@ -0,0 +1,360 @@ +/*************************************************************************** + alpha_mvi_crossblit.c Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +/** MUST be compiled with -mcpu=cv6! */ + +#include + + +#ifdef HAVE_ALPHA_EV6_SUPPORT + +#ifdef __DECC +# include "c_asm.h" +# define USE_C_CODE +#else /* GNU C */ +# undef USE_C_CODE +#endif + +void +FUNCT_NAME(byte *dest, byte *src, int bytes_per_dest_line, int bytes_per_src_line, + int xl, int yl, byte *alpha, int bytes_per_alpha_line, int bytes_per_alpha_pixel, + unsigned int alpha_test_mask, int alpha_shift +#ifdef PRIORITY + ,byte *priority_pos ,int bytes_per_priority_line, int bytes_per_priority_pixel, int priority +#endif + ) +{ +#ifdef USE_C_CODE +#ifdef PRIORITY + int left_mask = (255 << (((unsigned long) dest) & 7)) & 255; + int right_mask = (255 >> (((unsigned long) dest + bytes_per_dest_line) & 7)); +#endif + unsigned long data; + + assert(!(bytes_per_alpha_line & 7)); + assert(!(((unsigned long) src) & 3)); + assert(!(((unsigned long) dest) & 3)); + assert(bytes_per_alpha_pixel < 2); + + yl++; + while (--yl) { + int x; + byte *dest_next = dest + bytes_per_dest_line; + byte *src_next = src + bytes_per_src_line; +#ifdef PRIORITY + byte *pri_next = priority_pos + bytes_per_priority_line; +#endif + asm ("ldl $31, 0($31)\n\t" + "ldl $31, 0($31)\n\t"); /* Prefetch memory for next line */ + + if (((unsigned long)src) & 4) + data = *((unsigned int *) src); + + for (x = xl; x > 0; x--) { + unsigned long alpha; + + if (!(((unsigned long)src) & 4)) + data = *((unsigned long *) src); + alpha = (data & alpha_test_mask) >> alpha_shift; + + if ( +#ifdef PRIORITY + (*priority_pos <= priority) && +#endif + alpha != 255) { + unsigned long result; + unsigned long orig; + unsigned long unpkdata; + + unpkdata = asm("unpkbw %0, %v0\n\t", data); + + result = unpkdata * (255 - alpha); + + orig = *((unsigned int *) dest); + + orig = asm("unpkbw %0, %v0\n\t", orig); + + result += orig * alpha; + src += 4; + + result >>= 8; + + result = asm("pkwb %0, %v0\n\t", result); + + data >>= 32; + *((unsigned int *) dest) = result; +#ifdef PRIORITY + *priority_pos = priority; +#endif + dest += 4; + } else { + data >>= 32; + src += 4; + dest += 4; + } + +#ifdef PRIORITY + priority_pos++; +#endif + } + + dest = dest_next; + src = src_next; +#ifdef PRIORITY + priority_pos = pri_next; +#endif + } +#else + unsigned long real_alpha_shift = alpha_shift; +#ifdef PRIORITY + assert(!(bytes_per_alpha_line & 7)); + assert(bytes_per_alpha_pixel < 2); + real_alpha_shift |= (unsigned long) (priority << 16) | ((unsigned long) bytes_per_priority_pixel << 32); + /* Work around gcc design bug allowing only 10 asm parameters */ +#endif + __asm__ __volatile__ ( +#ifdef PRIORITY + /* + ** dest: $16 + ** src: $17 + ** bytes_per_dest_line: $18 + ** bytes_per_src_line: $19 + ** xl : $20 + ** yl : $21 + ** alpha_test_mask: $24 + ** alpha_shift: $25 + ** 255: $8 + ** + ** bytes_per_priority_line: $9 + ** priority_pos: $10 + ** priority extended to 8 bytes: $7 + ** bytes_per_priority_pixel: $6 + ** + ** temp_priority_collection: $11 + ** priority_pos backup: $12 + ** left border mask: $13 + ** right border mask: $28 + ** priority test bit: $15 + ** ldq4priority result: $26 + */ + + "lda $30, -88($30) \n\t" + "stq $9, 0($30) \n\t" + "stq $10, 8($30) \n\t" + "stq $11, 16($30) \n\t" + "stq $12, 24($30) \n\t" + "stq $13, 32($30) \n\t" + "stq $15, 40($30) \n\t" + "stq $26, 48($30) \n\t" + "stq %8, 56($30) \n\t" + "stq %9, 64($30) \n\t" + "stq $7, 72($30) \n\t" + "stq $6, 80($30) \n\t" + + "mov %8, $9 \n\t" + "mov %9, $10 \n\t" +#endif + "mov %6, $24 \n\t" + "mov %7, $25 \n\t" + "mov 255, $8 \n\t" + "subl $21, 1, $21 \n\t" +#ifdef PRIORITY + + /* Defrobnicate real_alpha_shift data */ + "srl $25, 32, $6 \n\t" + "srl $25, 16, $7 \n\t" + "and $7, $8, $7 \n\t" + + /* Load first priority data quad */ + "andnot $10, 7, $0 \n\t" + "ldq $26, 0($0) \n\t" /* Load priority */ + "and $10, 7, $13 \n\t" + "sll $13, 3, $0 \n\t" + + /* Calculate right border mask */ + "addl $13, $20, $28\n\t" + "and $28, 7, $28 \n\t" + "beq $28, 7f \n\t" + "mov 8, $0 \n\t" + "subl $0, $28, $28 \n" + "7:\n\t" + "srl $8, $28, $28 \n\t" + /* Left border mask */ + "sll $8, $13, $13 \n\t" + "and $13, $8, $13 \n\t" + + "mov $10, $12 \n\t" + + "sll $7, 8, $0 \n\t" + "or $7, $0, $7 \n\t" + "sll $7, 16, $0 \n\t" + "or $7, $0, $7 \n\t" + "sll $7, 32, $0 \n\t" + "or $7, $0, $7 \n\t" + "cmpbge $7, $26, $3 \n\t" + + "and $10, 7, $0 \n\t" /* Init priority bitptr */ + "mov 1, $15 \n\t" /* .. */ + "sll $15, $0, $15 \n\t" /* .. */ + + /* -> Priority buffer */ + "and $3, $13, $11 \n\t" + "cmplt $20, 8, $0 \n\t" + "beq $0, 6f \n\t" + "and $11, $28, $11 \n\t" + "6:\n\t" +#endif + "and $25, $8, $25 \n\t" + + /***/ + /*** Variable settings apply NOW ***/ + /***/ + + "mov $20, $1 \n\t" + "mov $16, $2 \n\t" + "mov $17, $3 \n" + "8:\n\t" + "addq $2, $18, $2 \n\t" + "ldl $31, 0($2) \n\t" /* Prefetch dest */ + "addq $3, $19, $3 \n\t" + "ldl $31, 0($3) \n" /* Prefetch src */ + "1:\n\t" + "addq $17, 4, $17 \n\t" + "beq $20, 3f \n\t" + "subl $20, 1, $20 \n\t" +#ifdef PRIORITY + "5:\n\t" + "addq $10, 1, $10 \n\t" + "and $11, $15, $0 \n\t" /* Other priority beat our priority? */ + "beq $0, 2f \n\t" +#endif + "ldl $0, -4($17) \n\t" + "unpkbw $0, $5 \n\t" + "and $0, $24, $0 \n\t" + + "xor $0, $24, $4 \n\t" + "beq $4, 2f \n\t" + + "ldl $4, 0($16) \n\t" + "unpkbw $4, $4 \n\t" + "srl $0, $25, $0 \n\t" + "mulq $4, $0, $4 \n\t" + "subl $8, $0, $0 \n\t" + "mulq $5, $0, $5 \n\t" + "addq $4, $5, $4 \n\t" + "srl $4, 8, $4 \n\t" + "pkwb $4, $0 \n\t" + "stl $0, 0($16) \n\t" + "br 9f \n\t" + "2:\n" + "andnot $11, $15, $11 \n\t" /* Don't draw priority if we're fully transparent */ + "9:\n\t" + "addq $16, 4, $16 \n\t" +#ifdef PRIORITY + "sll $15, 1, $15 \n\t" + + "and $10, 7, $0 \n\t" /* Do we need to re-load priority mask? */ +/**/ "bne $0, 1b \n\t" + + /* Write back to priority buffer */ + "zap $26, $11, $26 \n\t" + "zapnot $7, $11, $0 \n\t" + "or $0, $26, $0 \n\t" + "stq $0, -8($10) \n\t" + + "ldq $26, 0($10) \n\t" /* Load priority */ + "cmpbge $7, $26, $11\n\t" + + "mov 1, $15 \n\t" /* Init bitcmpmask */ + + "cmplt $20, 8, $0 \n\t" +/**/ "beq $0, 1b \n\t" + "and $11, $28, $11 \n\t" +#endif + "br 1b \n" + "3:\n\t" +#ifdef PRIORITY + "and $10, 7, $16 \n\t" + "beq $16, 7f \n\t" + "and $11, $28, $11 \n\t" + "zap $26, $11, $26 \n\t" + "zapnot $7, $11, $0 \n\t" + "or $0, $26, $0 \n\t" + "andnot $10, 7, $16 \n\t" + "stq $0, 0($16) \n" /* Write back */ + "7:\n\t" + + "addq $9, $12, $12 \n\t" + "mov $12, $10 \n\t" + "andnot $10, 7, $0 \n\t" + "ldq $26, 0($0) \n\t" + + "and $10, 7, $0 \n\t" + "mov 1, $15 \n\t" + "sll $15, $0, $15 \n\t" /* Store priority-induced write-enable mask */ + + "cmpbge $7, $26, $16 \n\t" + /* -> Priority buffer */ + "and $16, $13, $11 \n\t" +#endif + "beq $21, 4f \n\t" + "subl $21, 1, $21 \n\t" + "mov $1, $20 \n\t" + "mov $2, $16 \n\t" /* Set line numbers for next line */ + "mov $3, $17 \n\t" + "br 8b \n" + "4:\n\t" +#ifdef PRIORITY + "ldq $9, 0($30) \n\t" + "ldq $10, 8($30) \n\t" + "ldq $11, 16($30) \n\t" + "ldq $12, 24($30) \n\t" + "ldq $13, 32($30) \n\t" + "ldq $15, 40($30) \n\t" + "ldq $26, 48($30) \n\t" + "ldq %8, 56($30) \n\t" + "ldq %9, 64($30) \n\t" + "ldq $7, 72($30) \n\t" + "ldq $6, 80($30) \n\t" + "lda $30, 88($30) \n\t" +#endif + : + : + "r"(dest), "r"(src), "r"(bytes_per_dest_line), + /*3*/ "r"(bytes_per_src_line), "r"(xl), "r"(yl), + /*6*/ "r"((unsigned long) alpha_test_mask), "r"(real_alpha_shift) +#ifdef PRIORITY + , "r"(bytes_per_priority_line), "r"(priority_pos) +#endif + : "%0", "%1", "%2", "%3", "%4", "%5", "%6", + "%7", "$16", "$17", "$18", "$19", "$20", "$21", "$24", "$25", + "$8", "memory" + ); +#endif +} + +#endif /* __alpha__ */ diff --git a/engines/sci/gfx/antialias.c b/engines/sci/gfx/antialias.c new file mode 100644 index 0000000000..ad264d18bd --- /dev/null +++ b/engines/sci/gfx/antialias.c @@ -0,0 +1,163 @@ +/*************************************************************************** + antialias.c Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +/** Antialiasing code **/ + +#include +#include + +static void +antialiase_simple(gfx_pixmap_t *pixmap, int mask[], int shift_const, gfx_mode_t *mode) +{ + int x, y, c; + int bytespp = mode->bytespp; + int line_size = bytespp * pixmap->xl; + char *lastline[2]; + char *lastline_p = NULL; + char *data_p = (char *) pixmap->data; + + lastline[0] = (char*)sci_malloc(line_size); + lastline[1] = (char*)sci_malloc(line_size); + + for (y = 0; y < pixmap->yl; y++) { + int visimode = (y > 0 && y+1 < pixmap->yl)? 1 : 0; + unsigned long last_pixel; + + memcpy(lastline[y & 1], data_p, line_size); + lastline_p = lastline[(y & 1)^1]; + + for (x = 0; x < pixmap->xl; x++) { + unsigned long result = 0; + + if (x == 1) + visimode++; + else if (x+1 == pixmap->xl) + visimode--; + + for (c = 0; c < 3; c++) { + unsigned long accum = 0; + unsigned long reader = 0; + int y_mode; + + /* Yes, bad compilers will read three times as often as neccessary. + ** This optimization is straightforward to detect (common subexpression + ** elemination), so I prefer to write the stuff semi-legibly... + */ + for (y_mode = 0; y_mode < 2; y_mode++) + if ((y_mode == 0 && y > 0) + || (y_mode == 1 && y+1 < pixmap->yl)) { + + char *src = (y_mode)? data_p + line_size : lastline_p; + + if (x > 0) { + memcpy(&reader, src - bytespp, bytespp); + accum += ((reader >> shift_const) & mask[c]) << 0; + } + + memcpy(&reader, src, bytespp); + accum += ((reader >> shift_const) & mask[c]) << 1; + + if (x+1 < pixmap->xl) { + memcpy(&reader, src + bytespp, bytespp); + accum += ((reader >> shift_const) & mask[c]) << 0; + } + } + + if (x > 0) + accum += ((last_pixel >> shift_const) & mask[c]) << 1; + + memcpy(&reader, data_p, bytespp); + if (c == 2) + last_pixel = reader; + accum += ((reader >> shift_const) & mask[c]) << 2; + + if (x+1 < pixmap->xl) { + memcpy(&reader, data_p + bytespp, bytespp); + accum += ((reader >> shift_const) & mask[c]) << 1; + } + + switch (visimode) { + + case 0: accum /= 9; /* Only happens twelve times */ + break; + + case 1: accum = (accum >> 6) + (accum >> 4); /* 15/16 intensity */ + break; + + case 2: accum >>= 4; + break; + + default: accum = (c == 0)? 0xffffffff : 0; /* Error: mark as red */ + } + + result |= (accum & mask[c]); + } + + result <<= shift_const; + memcpy(data_p, &result, bytespp); + + data_p += bytespp; + lastline_p += bytespp; + } + } + + free(lastline[0]); + free(lastline[1]); +} + +void +gfxr_antialiase(gfx_pixmap_t *pixmap, gfx_mode_t *mode, gfxr_antialiasing_t type) +{ + int masks[3]; + int shift_const = 0; + +#ifdef WORDS_BIGENDIAN + shift_const = (sizeof(unsigned long) - mode->bytespp) << 3; +#endif /* WORDS_BIGENDIAN */ + + masks[0] = mode->red_mask; + masks[1] = mode->green_mask; + masks[2] = mode->blue_mask; + + if (mode->palette) + return; + + switch (type) { + + case GFXR_ANTIALIASING_NONE: + break; + + case GFXR_ANTIALIASING_SIMPLE: + antialiase_simple(pixmap, masks, shift_const, mode); + break; + + default: + GFXERROR("Invalid antialiasing mode %d (internal error)\n", type); + } + + return; +} diff --git a/engines/sci/gfx/drivers/EXPORTS_ggi b/engines/sci/gfx/drivers/EXPORTS_ggi new file mode 100644 index 0000000000..0f9ed580f6 --- /dev/null +++ b/engines/sci/gfx/drivers/EXPORTS_ggi @@ -0,0 +1 @@ +gfx_driver_ggi diff --git a/engines/sci/gfx/drivers/EXPORTS_sdl b/engines/sci/gfx/drivers/EXPORTS_sdl new file mode 100644 index 0000000000..55e1950033 --- /dev/null +++ b/engines/sci/gfx/drivers/EXPORTS_sdl @@ -0,0 +1 @@ +gfx_driver_sdl diff --git a/engines/sci/gfx/drivers/EXPORTS_xlib b/engines/sci/gfx/drivers/EXPORTS_xlib new file mode 100644 index 0000000000..5d705799b0 --- /dev/null +++ b/engines/sci/gfx/drivers/EXPORTS_xlib @@ -0,0 +1,2 @@ +gfx_driver_xlib + diff --git a/engines/sci/gfx/drivers/Makefile.am b/engines/sci/gfx/drivers/Makefile.am new file mode 100644 index 0000000000..c5dbbe0e9f --- /dev/null +++ b/engines/sci/gfx/drivers/Makefile.am @@ -0,0 +1,32 @@ +EXTRA_DIST = dc_driver.c dd_driver.cpp dd_driver.h dd_driver_line.cpp dx_driver.cpp dx_driver.h +INCLUDES = -I$(top_srcdir)/src/include @ac_ggi_includes@ @ac_png_includes@ @X_CFLAGS@ @EXTRA_INCLUDES@ +AM_CFLAGS = $(SDL_CFLAGS) +LDADD = $(LDADD_ALL) + +gfxdriverdir = $(libdir)/freesci/gfx + +#EXTRA_LTLIBRARIES = xlib_driver.la ggi_driver.la sdl_driver.la +#gfxdriver_LTLIBRARIES = @fsci_xlib_dl@ @fsci_ggi_dl@ @fsci_sdl_dl@ + +noinst_LIBRARIES = libscidrivers.a +libscidrivers_a_SOURCES = gfx_drivers.c xlib_driver.c ggi_driver.c sdl_driver.c directfb_driver.c \ + null_driver.c +#libscidrivers_a_OBJECTS = gfx_drivers.o @fsci_xlib_st@ @fsci_ggi_st@ @fsci_sdl_st@ + + +#xlib_driver_la_LDFLAGS = -rpath $(gfxdriverdir) -module -no-undefined \ +# -export-symbols $(srcdir)/EXPORTS_xlib +#xlib_driver_la_LIBADD = @X_LIBS@ +#xlib_driver_la_SOURCES = xlib_driver.c + +#ggi_driver_la_LDFLAGS = -rpath $(gfxdriverdir) -module -no-undefined \ +# -export-symbols $(srcdir)/EXPORTS_ggi +#ggi_driver_la_LIBADD = @X_LIBS@ @ac_ggi_libraries@ +#ggi_driver_la_SOURCES = ggi_driver.c + +#sdl_driver_la_LDFLAGS = -rpath $(gfxdriverdir) -module -no-undefined \ +# -export-symbols $(srcdir)/EXPORTS_sdl +#sdl_driver_la_LIBADD = @SDL_LIBS@ +#3sdl_driver_la_SOURCES = sdl_driver.c + + diff --git a/engines/sci/gfx/drivers/dc_driver.c b/engines/sci/gfx/drivers/dc_driver.c new file mode 100644 index 0000000000..5f2b4419ae --- /dev/null +++ b/engines/sci/gfx/drivers/dc_driver.c @@ -0,0 +1,1267 @@ +/*************************************************************************** + dc_driver.c Copyright (C) 2002-2005 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "keyboard.h" + +/* Event queue struct */ + +struct dc_event_t { + sci_event_t event; + struct dc_event_t *next; +}; + +#define SCI_DC_RENDER_PVR (1 << 0) /* 0 = VRAM rendering, 1 = PVR */ +#define SCI_DC_REFRESH_50HZ (1 << 1) /* 0 = 60Hz refresh rate, 1 = 50Hz */ + +static int flags = 0; + +struct _dc_state { + /* 0 = static buffer, 1 = back buffer, 2 = front buffer */ + /* Visual maps */ + byte *visual[3]; + /* Priority maps */ + byte *priority[2]; + /* Line pitch of visual buffers */ + int line_pitch[3]; + + /* PVR only */ + /* Polygon header */ + pvr_poly_hdr_t pvr_hdr; + /* Polygon header for virtual keyboard */ + pvr_poly_hdr_t pvr_hdr_vkbd; + /* Texture for virtual keyboard */ + uint16 *vkbd_txr; + + /* Pointers to first and last event in the event queue */ + struct dc_event_t *first_event; + struct dc_event_t *last_event; + + /* Semaphores for mouse pointer location and event queue updates */ + semaphore_t *sem_event, *sem_pointer; + + /* The dx and dy of the mouse pointer since last get_event() */ + int pointer_dx, pointer_dy; + + /* The current bucky state of the keys */ + int buckystate; + + /* Thread ID of the input thread */ + kthread_t *thread; + + /* Flag to stop the input thread. (!0 = run, 0 = stop) */ + int run_thread; + + /* Controller key repeat timer */ + int timer; + + /* Controller state */ + unsigned int cont_state; + + /* Virtual keyboard on/off flag */ + int vkbd; +}; + +#define S ((struct _dc_state *)(drv->state)) + +#define XFACT drv->mode->xfact +#define YFACT drv->mode->yfact +#define BYTESPP drv->mode->bytespp + +#define DC_MOUSE_LEFT (1<<0) +#define DC_MOUSE_RIGHT (1<<1) + +#define DC_KEY_CAPSLOCK (1<<0) +#define DC_KEY_NUMLOCK (1<<1) +#define DC_KEY_SCRLOCK (1<<2) +#define DC_KEY_INSERT (1<<3) + +static void +pvr_init_gfx(struct _gfx_driver *drv, int xfact, int yfact, int bytespp) +/* Initialises the graphics driver for PVR rendering mode +** Parameters: (_gfx_driver *) drv: The driver to use +** (int) xfact, yfact: X and Y scaling factors. (xfact, yfact) +** has to be either (1, 1) or (2, 2). +** (int) bytespp: The display depth in bytes per pixel. Must be 2. +** Returns : void +*/ +{ + pvr_poly_cxt_t cxt; + + /* Initialize PVR to defaults and set background color to black */ + + if (flags & SCI_DC_REFRESH_50HZ) + vid_set_mode(DM_640x480_PAL_IL, PM_RGB565); + else + vid_set_mode(DM_640x480, PM_RGB565); + + pvr_init_defaults(); + pvr_set_bg_color(0,0,0); + + /* Allocate and initialize texture RAM */ + S->visual[2] = pvr_mem_malloc(512*256*bytespp*xfact*yfact); + S->line_pitch[2] = 512*bytespp*xfact; + memset(S->visual[2], 0, 512*256*bytespp*xfact*yfact); + + /* Create textured polygon context */ + pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_RGB565 | + PVR_TXRFMT_NONTWIDDLED, 512*xfact, 256*yfact, S->visual[2], 0); + + /* Create polygon header from context */ + pvr_poly_compile(&(S->pvr_hdr), &cxt); + + /* Allocate and initialize texture RAM for virtual keyboard */ + S->vkbd_txr = pvr_mem_malloc(512*64*bytespp*xfact*yfact); + memset(S->vkbd_txr, 0, 512*64*bytespp*xfact*yfact); + + /* Create textured polygon context for virtual keyboard */ + pvr_poly_cxt_txr(&cxt, PVR_LIST_TR_POLY, PVR_TXRFMT_RGB565 | + PVR_TXRFMT_NONTWIDDLED, 512*xfact, 64*yfact, S->vkbd_txr, 0); + + /* Create polygon header from context for virtual keyboard */ + pvr_poly_compile(&(S->pvr_hdr_vkbd), &cxt); + + vkbd_init((uint16 *) S->vkbd_txr, 512); + vkbd_draw(); +} + +static void +pvr_do_frame(struct _gfx_driver *drv) +/* Renders a frame for PVR rendering mode +** Parameters: (_gfx_driver *) drv: The driver to use +** Returns : void +*/ +{ + pvr_vertex_t vert; + + /* Wait until we can send another frame to the PVR */ + pvr_wait_ready(); + + /* Start a new scene */ + pvr_scene_begin(); + + /* Start an opaque polygon list */ + pvr_list_begin(PVR_LIST_OP_POLY); + + /* Submit polygon header */ + pvr_prim(&(S->pvr_hdr), sizeof(S->pvr_hdr)); + + /* Create and submit vertices */ + vert.flags = PVR_CMD_VERTEX; + vert.x = 0.0f; + vert.y = 480.0f; + vert.z = 1.0f; + vert.u = 0.0f; + vert.v = 0.78125f; + vert.argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f); + vert.oargb = 0; + pvr_prim(&vert, sizeof(vert)); + + vert.y = 0.0f; + vert.v = 0.0f; + pvr_prim(&vert, sizeof(vert)); + + vert.x = 640.0f; + vert.y = 480.0f; + vert.u = 0.625f; + vert.v = 0.78125f; + pvr_prim(&vert, sizeof(vert)); + + vert.flags = PVR_CMD_VERTEX_EOL; + vert.y = 0.0f; + vert.v = 0.0f; + pvr_prim(&vert, sizeof(vert)); + + /* End list */ + pvr_list_finish(); + + /* Display virtual keyboard */ + if (S->vkbd) { + /* Start an translucent polygon list */ + pvr_list_begin(PVR_LIST_TR_POLY); + + /* Submit polygon header */ + pvr_prim(&(S->pvr_hdr_vkbd), sizeof(S->pvr_hdr_vkbd)); + + /* Create and submit vertices */ + vert.flags = PVR_CMD_VERTEX; + vert.x = 0.0f; + vert.y = 480.0f; + vert.z = 1.0f; + vert.u = 0.0f; + vert.v = 0.625f; + vert.argb = PVR_PACK_COLOR(0.8f, 1.0f, 1.0f, 1.0f); + vert.oargb = 0; + pvr_prim(&vert, sizeof(vert)); + + vert.y = 400.0f; + vert.v = 0.0f; + pvr_prim(&vert, sizeof(vert)); + + vert.x = 640.0f; + vert.y = 480.0f; + vert.u = 0.625f; + vert.v = 0.625f; + pvr_prim(&vert, sizeof(vert)); + + vert.flags = PVR_CMD_VERTEX_EOL; + vert.y = 400.0f; + vert.v = 0.0f; + pvr_prim(&vert, sizeof(vert)); + + /* End list */ + pvr_list_finish(); + } + + /* End scene */ + pvr_scene_finish(); +} + +static void +vram_init_gfx(struct _gfx_driver *drv, int xfact, int yfact, int bytespp) +/* Initialises the graphics driver for VRAM rendering mode +** Parameters: (_gfx_driver *) drv: The driver to use +** (int) xfact, yfact: X and Y scaling factors. (xfact, yfact) +** has to be either (1, 1) or (2, 2). +** (int) bytespp: The display depth in bytes per pixel. Must be +** either 2 or 4. +** Returns : void +*/ +{ + int vidres = 0, vidcol = 0; + + /* Center screen vertically */ + S->visual[2] = (byte *) vram_s+320*xfact*20*yfact*bytespp; + + S->line_pitch[2] = 320*xfact*bytespp; + + memset(S->visual[2], 0, 320*xfact*240*yfact*bytespp); + + switch(bytespp) { + case 2: + vidcol = PM_RGB565; + break; + case 4: + vidcol = PM_RGB888; + } + + if (flags & SCI_DC_REFRESH_50HZ) switch (xfact) { + case 1: + vidres = DM_320x240_PAL; + break; + case 2: + vidres = DM_640x480_PAL_IL; + } + else switch (xfact) { + case 1: + vidres = DM_320x240; + break; + case 2: + vidres = DM_640x480; + } + + vid_set_mode(vidres, vidcol); + + vkbd_init((uint16 *) (S->visual[2] + 320 * xfact * 200 * yfact * bytespp), 320); +} + +static void +vram_hide_keyboard(struct _gfx_driver *drv) +/* Hides the virtual keyboard in VRAM rendering mode +** Parameters: (_gfx_driver *) drv: The driver to use +** Returns : void +*/ +{ + vid_set_start(0); + memset4(S->visual[2] + 320 * XFACT * 200 * YFACT * BYTESPP, 0, 320 * XFACT * 40 * YFACT * BYTESPP); +} + +static void +vram_show_keyboard(struct _gfx_driver *drv) +/* Displays the virtual keyboard in VRAM rendering mode +** Parameters: (_gfx_driver *) drv: The driver to use +** Returns : void +*/ +{ + vid_set_start(320 * XFACT * 20 * YFACT * BYTESPP); + vkbd_draw(); +} + +static int +dc_add_event(struct _gfx_driver *drv, sci_event_t *event) +/* Adds an event to the end of an event queue +** Parameters: (_gfx_driver *) drv: The driver to use +** (sci_event_t *) event: The event to add +** Returns : (int) 1 on success, 0 on error +*/ +{ + struct dc_event_t *dc_event; + if (!(dc_event = sci_malloc(sizeof(dc_event)))) { + printf("Error: Could not reserve memory for event\n"); + return 0; + } + + dc_event->event = *event; + dc_event->next = NULL; + + /* Semaphore prevents get_event() from removing the last event in + ** the event queue while a next event is being attached to it. + */ + + sem_wait(S->sem_event); + if (!(S->last_event)) { + /* Event queue is empty */ + S->first_event = dc_event; + S->last_event = dc_event; + sem_signal(S->sem_event); + return 1; + } + + S->last_event->next = dc_event; + S->last_event = dc_event; + sem_signal(S->sem_event); + return 1; +} + +static int +dc_map_key(int *keystate, uint8 key) +/* Converts a kos keycode to a freesci keycode. This function also adjusts +** the caps lock, num lock, scroll lock and insert states in keystate. +** Parameters: (int *) keystate: Pointer to the keystate variable +** (uint8) key: The kos keycode to convert +** Returns : (int) Converted freesci keycode on success, 0 on error +*/ +{ + if ((key >= KBD_KEY_A) && (key <= KBD_KEY_Z)) + return 'a' + (key - KBD_KEY_A); + + if ((key >= KBD_KEY_1) && (key <= KBD_KEY_9)) + return '1' + (key - KBD_KEY_1); + + switch (key) { + case KBD_KEY_0: return '0'; + case KBD_KEY_BACKSPACE: return SCI_K_BACKSPACE; + case KBD_KEY_TAB: return 9; + case KBD_KEY_ESCAPE: return SCI_K_ESC; + case KBD_KEY_ENTER: + case KBD_KEY_PAD_ENTER: return SCI_K_ENTER; + case KBD_KEY_DEL: + case KBD_KEY_PAD_PERIOD: return SCI_K_DELETE; + case KBD_KEY_INSERT: + case KBD_KEY_PAD_0: *keystate ^= DC_KEY_INSERT; + return SCI_K_INSERT; + case KBD_KEY_END: + case KBD_KEY_PAD_1: return SCI_K_END; + case KBD_KEY_DOWN: + case KBD_KEY_PAD_2: return SCI_K_DOWN; + case KBD_KEY_PGDOWN: + case KBD_KEY_PAD_3: return SCI_K_PGDOWN; + case KBD_KEY_LEFT: + case KBD_KEY_PAD_4: return SCI_K_LEFT; + case KBD_KEY_PAD_5: return SCI_K_CENTER; + case KBD_KEY_RIGHT: + case KBD_KEY_PAD_6: return SCI_K_RIGHT; + case KBD_KEY_HOME: + case KBD_KEY_PAD_7: return SCI_K_HOME; + case KBD_KEY_UP: + case KBD_KEY_PAD_8: return SCI_K_UP; + case KBD_KEY_PGUP: + case KBD_KEY_PAD_9: return SCI_K_PGUP; + case KBD_KEY_F1: return SCI_K_F1; + case KBD_KEY_F2: return SCI_K_F2; + case KBD_KEY_F3: return SCI_K_F3; + case KBD_KEY_F4: return SCI_K_F4; + case KBD_KEY_F5: return SCI_K_F5; + case KBD_KEY_F6: return SCI_K_F6; + case KBD_KEY_F7: return SCI_K_F7; + case KBD_KEY_F8: return SCI_K_F8; + case KBD_KEY_F9: return SCI_K_F9; + case KBD_KEY_F10: return SCI_K_F10; + case KBD_KEY_PAD_PLUS: return '+'; + case KBD_KEY_SLASH: + case KBD_KEY_PAD_DIVIDE: return '/'; + case KBD_KEY_MINUS: + case KBD_KEY_PAD_MINUS: return '-'; + case KBD_KEY_PAD_MULTIPLY: return '*'; + case KBD_KEY_COMMA: return ','; + case KBD_KEY_PERIOD: return '.'; + case KBD_KEY_BACKSLASH: return '\\'; + case KBD_KEY_SEMICOLON: return ';'; + case KBD_KEY_QUOTE: return '\''; + case KBD_KEY_LBRACKET: return '['; + case KBD_KEY_RBRACKET: return ']'; + case KBD_KEY_TILDE: return '`'; + case KBD_KEY_PLUS: return '='; + case KBD_KEY_SPACE: return ' '; + case KBD_KEY_CAPSLOCK: *keystate ^= DC_KEY_CAPSLOCK; + return 0; + case KBD_KEY_SCRLOCK: *keystate ^= DC_KEY_SCRLOCK; + return 0; + case KBD_KEY_PAD_NUMLOCK: *keystate ^= DC_KEY_NUMLOCK; + return 0; + } + + printf("Warning: Unmapped key: %02x\n", key); + + return 0; +} + +static void +dc_input_thread(struct _gfx_driver *drv) +/* Thread that checks the dreamcast keyboard and mouse states. It adds +** keypresses and mouseclicks to the end of the event queue. It also updates +** drv->state.buckystate and drv->state.pointer_dx/dy. +** Parameters: (_gfx_driver *) drv: The driver to use +** Returns : void +*/ +{ + /* State of mouse buttons */ + unsigned int mstate = 0; + /* Last key pressed */ + unsigned int lastkey = KBD_KEY_NONE; + /* State of caps lock, scroll lock, num lock and insert keys */ + int keystate = DC_KEY_INSERT; + + while (S->run_thread) { + maple_device_t *kaddr = NULL, *maddr, *caddr; + mouse_state_t *mouse; + kbd_state_t *kbd = 0; + cont_state_t *cont; + uint8 key; + int skeys; + int bucky = 0, vkbd_bucky = 0; + sci_event_t event; + + if (!(flags & SCI_DC_RENDER_PVR)) + /* Sleep for 10ms */ + thd_sleep(10); + else + pvr_do_frame(drv); + + /* Keyboard handling */ + /* Experimental workaround for the Mad Catz adapter problem */ + if (!kaddr) + kaddr = maple_enum_type(0, MAPLE_FUNC_KEYBOARD); + if (kaddr && (kbd = maple_dev_status(kaddr))) { + key = kbd->cond.keys[0]; + skeys = kbd->shift_keys; + + bucky = ((skeys & (KBD_MOD_LCTRL | KBD_MOD_RCTRL))? + SCI_EVM_CTRL : 0) | + ((skeys & (KBD_MOD_LALT | KBD_MOD_RALT))? + SCI_EVM_ALT : 0) | + ((skeys & KBD_MOD_LSHIFT)? + SCI_EVM_LSHIFT : 0) | + ((skeys & KBD_MOD_RSHIFT)? + SCI_EVM_RSHIFT : 0) | + ((keystate & DC_KEY_NUMLOCK)? + SCI_EVM_NUMLOCK : 0) | + ((keystate & DC_KEY_SCRLOCK)? + SCI_EVM_SCRLOCK : 0) | + ((keystate & DC_KEY_INSERT)? + SCI_EVM_INSERT : 0); + + /* If a shift key is pressed when caps lock is on, set + ** both shift key states to 0. If no shift keys are + ** pressed when caps lock is on, set both shift key + ** states to 1 + */ + + if (keystate & DC_KEY_CAPSLOCK) { + if ((bucky & SCI_EVM_LSHIFT) || + (bucky & SCI_EVM_RSHIFT)) + bucky &= + ~(SCI_EVM_LSHIFT | SCI_EVM_RSHIFT); + else bucky |= SCI_EVM_LSHIFT | SCI_EVM_RSHIFT; + } + + if ((key != lastkey) && (key != KBD_KEY_NONE)) { + event.type = SCI_EVT_KEYBOARD; + event.data = dc_map_key(&keystate, key); + event.buckybits = bucky | vkbd_bucky; + if (event.data) dc_add_event(drv, &event); + } + lastkey = key; + } + else kaddr = NULL; + + /* Mouse handling */ + if ((maddr = maple_enum_type(0, MAPLE_FUNC_MOUSE)) && + (mouse = maple_dev_status(maddr))) { + + /* Enable mouse support */ + drv->capabilities |= GFX_CAPABILITY_MOUSE_SUPPORT; + + /* Semaphore prevents get_event() from accessing + ** S->pointer_dx/dy while they are being updated + */ + sem_wait(S->sem_pointer); + S->pointer_dx += mouse->dx; + S->pointer_dy += mouse->dy; + sem_signal(S->sem_pointer); + + if ((mouse->buttons & MOUSE_LEFTBUTTON) && + !(mstate & DC_MOUSE_LEFT)) { + event.type = SCI_EVT_MOUSE_PRESS; + event.data = 1; + event.buckybits = bucky | vkbd_bucky; + dc_add_event(drv, &event); + mstate |= DC_MOUSE_LEFT; + } + if ((mouse->buttons & MOUSE_RIGHTBUTTON) && + !(mstate & DC_MOUSE_RIGHT)) { + event.type = SCI_EVT_MOUSE_PRESS; + event.data = 2; + event.buckybits = bucky | vkbd_bucky; + dc_add_event(drv, &event); + mstate |= DC_MOUSE_RIGHT; + } + if (!(mouse->buttons & MOUSE_LEFTBUTTON) && + (mstate & DC_MOUSE_LEFT)) { + event.type = SCI_EVT_MOUSE_RELEASE; + event.data = 1; + event.buckybits = bucky | vkbd_bucky; + dc_add_event(drv, &event); + mstate &= ~DC_MOUSE_LEFT; + } + if (!(mouse->buttons & MOUSE_RIGHTBUTTON) && + (mstate & DC_MOUSE_RIGHT)) { + event.type = SCI_EVT_MOUSE_RELEASE; + event.data = 2; + event.buckybits = bucky | vkbd_bucky; + dc_add_event(drv, &event); + mstate &= ~DC_MOUSE_RIGHT; + } + } + else if ((caddr = maple_enum_type(0, MAPLE_FUNC_CONTROLLER)) && + (cont = maple_dev_status(caddr))) { + /* Enable mouse support */ + drv->capabilities |= GFX_CAPABILITY_MOUSE_SUPPORT; + + /* Semaphore prevents get_event() from accessing + ** S->pointer_dx/dy while they are being updated + */ + sem_wait(S->sem_pointer); + S->pointer_dx += cont->joyx/20; + S->pointer_dy += cont->joyy/20; + sem_signal(S->sem_pointer); + + if ((cont->ltrig > 5) && + !(mstate & DC_MOUSE_LEFT)) { + event.type = SCI_EVT_MOUSE_PRESS; + event.data = 1; + event.buckybits = bucky | vkbd_bucky; + dc_add_event(drv, &event); + mstate |= DC_MOUSE_LEFT; + } + if ((cont->rtrig > 5) && + !(mstate & DC_MOUSE_RIGHT)) { + event.type = SCI_EVT_MOUSE_PRESS; + event.data = 2; + event.buckybits = bucky | vkbd_bucky; + dc_add_event(drv, &event); + mstate |= DC_MOUSE_RIGHT; + } + if ((cont->ltrig <= 5) && + (mstate & DC_MOUSE_LEFT)) { + event.type = SCI_EVT_MOUSE_RELEASE; + event.data = 1; + event.buckybits = bucky | vkbd_bucky; + dc_add_event(drv, &event); + mstate &= ~DC_MOUSE_LEFT; + } + if ((cont->rtrig <= 5) && + (mstate & DC_MOUSE_RIGHT)) { + event.type = SCI_EVT_MOUSE_RELEASE; + event.data = 2; + event.buckybits = bucky | vkbd_bucky; + dc_add_event(drv, &event); + mstate &= ~DC_MOUSE_RIGHT; + } + if (S->timer > 0) + S->timer--; + if ((cont->buttons != S->cont_state) || !S->timer) { + S->cont_state = cont->buttons; + S->timer = 25; + if (cont->buttons & CONT_START) { + S->vkbd = S->vkbd ^ 1; + if (!(flags & SCI_DC_RENDER_PVR)) { + if(S->vkbd) + vram_show_keyboard(drv); + else + vram_hide_keyboard(drv); + } + } + if (S->vkbd) { + if (cont->buttons & CONT_DPAD_RIGHT) + vkbd_handle_input(KBD_RIGHT); + if (cont->buttons & CONT_DPAD_LEFT) + vkbd_handle_input(KBD_LEFT); + if (cont->buttons & CONT_DPAD_UP) + vkbd_handle_input(KBD_UP); + if (cont->buttons & CONT_DPAD_DOWN) + vkbd_handle_input(KBD_DOWN); + if (cont->buttons & CONT_A) { + int vkbd_key; + if (vkbd_get_key(&vkbd_key, &vkbd_bucky)) { + event.type = SCI_EVT_KEYBOARD; + event.data = vkbd_key; + event.buckybits = bucky | vkbd_bucky; + dc_add_event(drv, &event); + } + } + } + else { + event.data = 0; + if (cont->buttons & CONT_DPAD_RIGHT) + event.data = SCI_K_RIGHT; + else if (cont->buttons & CONT_DPAD_LEFT) + event.data = SCI_K_LEFT; + else if (cont->buttons & CONT_DPAD_UP) + event.data = SCI_K_UP; + else if (cont->buttons & CONT_DPAD_DOWN) + event.data = SCI_K_DOWN; + event.type = SCI_EVT_KEYBOARD; + event.buckybits = bucky | vkbd_bucky; + if (event.data) dc_add_event(drv, &event); + } + event.data = 0; + if (cont->buttons & CONT_B) + event.data = SCI_K_ENTER; + else if (cont->buttons & CONT_X) + event.data = ' '; + else if (cont->buttons & CONT_Y) + event.data = SCI_K_ESC; + event.type = SCI_EVT_KEYBOARD; + event.buckybits = bucky | vkbd_bucky; + if (event.data) + dc_add_event(drv, &event); + } + } + else drv->capabilities &= ~GFX_CAPABILITY_MOUSE_SUPPORT; + + S->buckystate = bucky | vkbd_bucky; + } +} + +static uint32 +dc_get_color(struct _gfx_driver *drv, gfx_color_t col) +/* Converts a color as described in col to it's representation in memory +** Parameters: (_gfx_driver *) drv: The driver to use +** (gfx_color_t) color: The color to convert +** Returns : (uint32) the color's representation in memory +*/ +{ + uint32 retval; + uint32 temp; + + retval = 0; + + temp = col.visual.r; + temp |= temp << 8; + temp |= temp << 16; + retval |= (temp >> drv->mode->red_shift) & (drv->mode->red_mask); + temp = col.visual.g; + temp |= temp << 8; + temp |= temp << 16; + retval |= (temp >> drv->mode->green_shift) & (drv->mode->green_mask); + temp = col.visual.b; + temp |= temp << 8; + temp |= temp << 16; + retval |= (temp >> drv->mode->blue_shift) & (drv->mode->blue_mask); + + return retval; +} + +static void +dc_draw_line_buffer(byte *buf, int line, int bytespp, int x1, + int y1, int x2, int y2, uint32 color) +/* Draws a line in a buffer +** This function was taken from sdl_driver.c with small modifications +** Parameters: (byte *) buf: The buffer to draw in +** (int) line: line pitch of buf in bytes +** (int) bytespp: number of bytes per pixel of buf +** (int) x1, y1, x2, y2: The line to draw: (x1,y1)-(x2,y2). +** (uint32) color: The color to draw with +** Returns : (void) +*/ +{ + int pixx, pixy; + int x,y; + int dx,dy; + int sx,sy; + int swaptmp; + uint8 *pixel; + + dx = x2 - x1; + dy = y2 - y1; + sx = (dx >= 0) ? 1 : -1; + sy = (dy >= 0) ? 1 : -1; + + dx = sx * dx + 1; + dy = sy * dy + 1; + pixx = bytespp; + pixy = line; + pixel = ((uint8*) buf) + pixx * x1 + pixy * y1; + pixx *= sx; + pixy *= sy; + if (dx < dy) { + swaptmp = dx; dx = dy; dy = swaptmp; + swaptmp = pixx; pixx = pixy; pixy = swaptmp; + } + + x=0; + y=0; + switch(bytespp) { + case 1: + for(; x < dx; x++, pixel += pixx) { + *pixel = color; + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + case 2: + for (; x < dx; x++, pixel += pixx) { + *(uint16*)pixel = color; + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + case 4: + for(; x < dx; x++, pixel += pixx) { + *(uint32*)pixel = color; + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + } + +} + +static void +dc_draw_filled_rect_buffer(byte *buf, int line, int bytespp, rect_t rect, + uint32 color) +/* Draws a filled rectangle in a buffer +** Parameters: (byte *) buf: The buffer to draw in +** (int) line: line pitch of buf in bytes +** (int) bytespp: number of bytes per pixel of buf +** (rect_t) rect: The rectangle to fill +** (uint32) color: The fill color +** Returns : (void) +*/ +{ + buf += rect.y*line + rect.x*bytespp; + int i; + + switch (bytespp) { + case 1: for (i = 0; istate /* = S */) + drv->state = sci_malloc(sizeof(struct _dc_state)); + if (!S) + return GFX_FATAL; + + if ((flags & SCI_DC_RENDER_PVR) && ((xfact != 1 && xfact != 2) + || bytespp != 2 || xfact != yfact)) { + sciprintf("Error: PVR rendering mode does not support " + "buffers with scale factors (%d,%d) and bpp=%d\n", + xfact, yfact, bytespp); + return GFX_ERROR; + } + else if ((xfact != 1 && xfact != 2) || (bytespp != 2 && bytespp != 4) + || xfact != yfact) { + sciprintf("Error: VRAM rendering mode does not support " + "buffers with scale factors (%d,%d) and bpp=%d\n", + xfact, yfact, bytespp); + return GFX_ERROR; + } + + for (i = 0; i < 2; i++) { + if (!(S->priority[i] = sci_malloc(320*xfact*200*yfact)) || + !(S->visual[i] = sci_malloc(320*xfact*200*yfact* bytespp))) { + sciprintf("Error: Could not reserve memory for buffer\n"); + return GFX_ERROR; + } + } + + for (i = 0; i < 2; i++) { + S->line_pitch[i] = 320*xfact*bytespp; + memset(S->visual[i], 0, 320*xfact*200*yfact*bytespp); + memset(S->priority[i], 0, 320*xfact*200*yfact); + } + + S->pointer_dx = 0; + S->pointer_dy = 0; + S->buckystate = 0; + S->timer = 0; + S->vkbd = 0; + + switch(bytespp) { + case 2: rmask = 0xF800; + gmask = 0x7E0; + bmask = 0x1F; + rshift = 16; + gshift = 21; + bshift = 27; + break; + case 4: rmask = 0xFF0000; + gmask = 0xFF00; + bmask = 0xFF; + rshift = 8; + gshift = 16; + bshift = 24; + } + + if (!(flags & SCI_DC_RENDER_PVR)) + vram_init_gfx(drv, xfact, yfact, bytespp); + else + pvr_init_gfx(drv, xfact, yfact, bytespp); + + drv->mode = gfx_new_mode(xfact, yfact, bytespp, rmask, gmask, bmask, 0, + rshift, gshift, bshift, 0, 0, 0); + + printf("Video mode initialisation completed succesfully\n"); + + S->run_thread = 1; + + S->thread = thd_create((void *) dc_input_thread, drv); + + S->first_event = NULL; + S->last_event = NULL; + + if (!(S->sem_event = sem_create(1)) || + !(S->sem_pointer = sem_create(1))) { + printf("Error: Could not reserve memory for semaphore\n"); + return GFX_ERROR; + }; + + return GFX_OK; +} + +static int +dc_init(struct _gfx_driver *drv) +{ + if (dc_init_specific(drv, 1, 1, 2) != GFX_OK) + return GFX_FATAL; + + return GFX_OK; +} + +static void +dc_exit(struct _gfx_driver *drv) +{ + if (S) { + sciprintf("Freeing graphics buffers\n"); + sci_free(S->visual[0]); + sci_free(S->visual[1]); + sci_free(S->priority[0]); + sci_free(S->priority[1]); + if (flags & SCI_DC_RENDER_PVR) { + pvr_mem_free(S->visual[2]); + pvr_mem_free(S->vkbd_txr); + } + + sciprintf("Waiting for input thread to exit... "); + S->run_thread = 0; + thd_wait(S->thread); + sciprintf("ok\n"); + + sciprintf("Freeing semaphores\n"); + sem_destroy(S->sem_event); + sem_destroy(S->sem_pointer); + sci_free(S); + drv->state /* = S */ = NULL; + } +} + + /*** Drawing operations ***/ + +static int +dc_draw_line(struct _gfx_driver *drv, point_t start, point_t end, + gfx_color_t color, gfx_line_mode_t line_mode, + gfx_line_style_t line_style) +{ + uint32 scolor; + int xfact = (line_mode == GFX_LINE_MODE_FINE)? 1: XFACT; + int yfact = (line_mode == GFX_LINE_MODE_FINE)? 1: YFACT; + int xsize = XFACT*320; + int ysize = YFACT*200; + + int xc, yc; + int x1, y1, x2, y2; + + scolor = dc_get_color(drv, color); + + for (xc = 0; xc < xfact; xc++) + for (yc = 0; yc < yfact; yc++) { + x1 = start.x + xc; + y1 = start.y + yc; + x2 = end.x + xc; + y2 = end.y + yc; + + if (x1 < 0) + x1 = 0; + if (x2 < 0) + x2 = 0; + if (y1 < 0) + y1 = 0; + if (y2 < 0) + y2 = 0; + + if (x1 > xsize) + x1 = xsize; + if (x2 >= xsize) + x2 = xsize - 1; + if (y1 > ysize) + y1 = ysize; + if (y2 >= ysize) + y2 = ysize - 1; + + if (color.mask & GFX_MASK_VISUAL) + dc_draw_line_buffer(S->visual[1], + XFACT*320*BYTESPP, BYTESPP, x1, y1, x2, y2, + dc_get_color(drv, color)); + + if (color.mask & GFX_MASK_PRIORITY) + dc_draw_line_buffer(S->priority[1], XFACT*320, + 1, x1, y1, x2, y2, color.priority); + } + + return GFX_OK; +} + +static int +dc_draw_filled_rect(struct _gfx_driver *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, gfx_rectangle_fill_t shade_mode) +{ + if (color1.mask & GFX_MASK_VISUAL) + dc_draw_filled_rect_buffer(S->visual[1], S->line_pitch[1], + BYTESPP, rect, dc_get_color(drv, color1)); + + if (color1.mask & GFX_MASK_PRIORITY) + dc_draw_filled_rect_buffer(S->priority[1], XFACT*320, 1, rect, + color1.priority); + + return GFX_OK; +} + + /*** Pixmap operations ***/ + +static int +dc_register_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + return GFX_ERROR; +} + +static int +dc_unregister_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + return GFX_ERROR; +} + +static int +dc_draw_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + int bufnr = (buffer == GFX_BUFFER_STATIC)? 0:1; + + return gfx_crossblit_pixmap(drv->mode, pxm, priority, src, dest, + S->visual[bufnr], S->line_pitch[bufnr], S->priority[bufnr], XFACT*320, + 1, 0); +} + +static int +dc_grab_pixmap(struct _gfx_driver *drv, rect_t src, gfx_pixmap_t *pxm, + gfx_map_mask_t map) +{ + switch (map) { + case GFX_MASK_VISUAL: + dc_copy_rect_buffer(S->visual[1], pxm->data, + S->line_pitch[1], src.xl*BYTESPP, BYTESPP, src, + gfx_point(0, 0)); + pxm->xl = src.xl; + pxm->yl = src.yl; + return GFX_OK; + case GFX_MASK_PRIORITY: + dc_copy_rect_buffer(S->priority[1], pxm->index_data, + XFACT*320, src.xl, 1, src, gfx_point(0, 0)); + pxm->index_xl = src.xl; + pxm->index_yl = src.yl; + return GFX_OK; + default: + sciprintf("Error: attempt to grab pixmap from invalid map"); + return GFX_ERROR; + } +} + + + /*** Buffer operations ***/ + +static int +dc_update(struct _gfx_driver *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + int tbufnr = (buffer == GFX_BUFFER_BACK)? 1:2; + + dc_copy_rect_buffer(S->visual[tbufnr-1], S->visual[tbufnr], + S->line_pitch[tbufnr-1], S->line_pitch[tbufnr], BYTESPP, src, dest); + + if ((tbufnr == 1) && (src.x == dest.x) && (src.y == dest.y)) + dc_copy_rect_buffer(S->priority[0], S->priority[1], XFACT*320, + XFACT*320, 1, src, dest); + + return GFX_OK; +} + +static int +dc_set_static_buffer(struct _gfx_driver *drv, gfx_pixmap_t *pic, gfx_pixmap_t *priority) +{ + memcpy4(S->visual[0], pic->data, XFACT*320 * YFACT*200 * BYTESPP); + memcpy4(S->priority[0], priority->index_data, XFACT*320 * YFACT*200); + return GFX_OK; +} + + /*** Palette operations ***/ + +static int +dc_set_palette(struct _gfx_driver *drv, int index, byte red, byte green, byte blue) +{ + return GFX_ERROR; +} + + + /*** Mouse pointer operations ***/ + +static int +dc_set_pointer (struct _gfx_driver *drv, gfx_pixmap_t *pointer) +{ + return GFX_ERROR; +} + + /*** Event management ***/ + +static sci_event_t +dc_get_event(struct _gfx_driver *drv) +{ + sci_event_t event; + struct dc_event_t *first; + sem_wait(S->sem_pointer); + drv->pointer_x += S->pointer_dx; + drv->pointer_y += S->pointer_dy; + S->pointer_dx = 0; + S->pointer_dy = 0; + sem_signal(S->sem_pointer); + + if (drv->pointer_x < 0) + drv->pointer_x = 0; + if (drv->pointer_x >= 320*XFACT) + drv->pointer_x = 320*XFACT-1; + if (drv->pointer_y < 0) + drv->pointer_y = 0; + if (drv->pointer_y >= 200*YFACT) + drv->pointer_y = 200*YFACT-1; + + sem_wait(S->sem_event); + first = S->first_event; + + if (first) { + event = first->event; + S->first_event = first->next; + free(first); + if (S->first_event == NULL) S->last_event = NULL; + sem_signal(S->sem_event); + return event; + } + + sem_signal(S->sem_event); + event.type = SCI_EVT_NONE; + event.buckybits = S->buckystate; + return event; +} + + +static int +dc_usec_sleep(struct _gfx_driver *drv, long usecs) +{ + /* TODO: wake up on mouse move */ + int ms = usecs/1000; + if (ms) + thd_sleep(ms); + return GFX_OK; +} + +gfx_driver_t +gfx_driver_dc = { + "dc", + "0.2b", + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, + 0, + 0, + GFX_CAPABILITY_FINE_LINES, + 0, + dc_set_parameter, + dc_init_specific, + dc_init, + dc_exit, + dc_draw_line, + dc_draw_filled_rect, + dc_register_pixmap, + dc_unregister_pixmap, + dc_draw_pixmap, + dc_grab_pixmap, + dc_update, + dc_set_static_buffer, + dc_set_pointer, + dc_set_palette, + dc_get_event, + dc_usec_sleep, + NULL +}; diff --git a/engines/sci/gfx/drivers/dd_driver.cpp b/engines/sci/gfx/drivers/dd_driver.cpp new file mode 100644 index 0000000000..f2920b4089 --- /dev/null +++ b/engines/sci/gfx/drivers/dd_driver.cpp @@ -0,0 +1,1021 @@ +#ifndef __cplusplus +#error NOTE: This file MUST be compiled as C++. In Visual C++, use the /Tp command line option. +#endif + +// for WINNT 4.0 (only DirectDraw 3) +#ifdef HAVE_DDRAW + +#define DIRECTDRAW_VERSION 0x0300 + +#define INITGUID + +#include +#include +#include + +extern "C" { +#include +#include +#include +#include +#include +#include +#include // for sciprintf +#include +}; + +#include "dd_driver.h" + +#define DD_DRIVER_VERSION "0.1" + +#define GFX_DD_DEBUG + + +#ifdef GFX_DD_DEBUG +#define POSMSG sciprintf("%s L%d:", __FILE__, __LINE__) +#define DEBUG_PTR (!(drv->debug_flags & GFX_DEBUG_POINTER))? 0 : POSMSG && sciprintf +#define DEBUG_UPDATES (!(drv->debug_flags & GFX_DEBUG_UPDATES))? 0 : POSMSG && sciprintf +#define DEBUG_PIXMAPS (!(drv->debug_flags & GFX_DEBUG_PIXMAPS))? 0 : POSMSG && sciprintf +#define DEBUG_BASIC (!(drv->debug_flags & GFX_DEBUG_BASIC))? 0 : POSMSG , sciprintf +#else /* !GFX_DD_DEBUG */ +#define DEBUG_PTR (1)? 0 : +#define DEBUG_UPDATES (1)? 0 : +#define DEBUG_PIXMAPS (1)? 0 : +#define DEBUG_BASIC (1)? 0 : +#endif /* !GFX_DD_DEBUG */ + +BOOL g_bFullScreen = FALSE; + +#define DD_BUFFER_BACK 0 +#define DD_BUFFER_STATIC 1 + +static long FAR PASCAL WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +static int process_messages(void); + +struct gfx_dd_struct_t +{ + LPDIRECTDRAW pDD; + HWND hMainWnd; + LPDIRECTDRAWSURFACE pPrimary; + LPDIRECTDRAWSURFACE pBack; + LPDIRECTDRAWSURFACE pStatic; + LPDIRECTDRAWCLIPPER pClipper; // used in windowed (not fullscreen) mode + int win_xpos,win_ypos; // current position of the window (windowed mode only) + BOOL bShowMouse; + gfx_pixmap_t *priority_maps[2]; + // event queue + int queue_size,queue_first,queue_last; + sci_event_t * event_queue; +}; + +static void init_event_queue(gfx_dd_struct_t *ctx); +static void free_event_queue(gfx_dd_struct_t *ctx); + +static __inline DWORD MakeRGB(gfx_mode_t *mode,unsigned int r,unsigned int g,unsigned int b) +{ + return ((r << mode->red_shift) & mode->red_mask) | + ((g << mode->green_shift) & mode->green_mask) | + ((b << mode->blue_shift) & mode->blue_mask); +}; + +static int +dd_set_param(gfx_driver_t *drv, char *attribute, char *value) +{ + DEBUG_BASIC("dd_set_param('%s' to '%s')\n", attribute, value); + return GFX_OK; +} + +static int +ShiftCount(DWORD mask) +{ + int cnt; + + if(mask==0) + return 0; // error !!! + + cnt=0; + + while((mask & 1)==0) + { + mask >>=1; + cnt++; + } + return cnt; +} + +static int +dd_init_specific(gfx_driver_t *drv, int xres, int yres, int bpp) +{ + DDSURFACEDESC dd_desc,ddsd; + HRESULT hr; + WNDCLASS wc; + RECT rc; + POINT pnt; + gfx_dd_struct_t *ctx; + int dd_bpp,r_sh,g_sh,b_sh,dd_bpp2,tbytes; +// const ggi_pixelformat *pixelformat; + int frames = 3; + + // force .... + xres=1; yres=1; bpp=1; + + if(g_bFullScreen) + { + // force the best mode + xres=1; yres=1; bpp=1; + } + switch(bpp) // bpp of 8,16,24,32 + { + case 1: + dd_bpp=8; break; + case 2: + dd_bpp=16; break; + case 3: + dd_bpp=24; break; + case 4: + dd_bpp=32; break; + default: + sciprintf("GFXDD: Error: Invalid bytes per pixel value: %d\n", bpp); + return GFX_ERROR; + } + + drv->state = NULL; + + ctx = (struct gfx_dd_struct_t *) sci_malloc(sizeof(gfx_dd_struct_t)); + if(ctx == NULL) + return GFX_FATAL; + + memset(ctx,0,sizeof(gfx_dd_struct_t)); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = sizeof(void *); + wc.hInstance = NULL; + wc.hIcon = LoadIcon (NULL, IDI_APPLICATION); + wc.hCursor = NULL; /*LoadCursor (NULL, IDC_ARROW)*/; + wc.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = "freesci.WndClass"; + RegisterClass (&wc); + + SetRect (&rc, 0, 0, xres*320-1, yres*200-1); + if(!g_bFullScreen) + { + AdjustWindowRectEx (&rc, WS_OVERLAPPEDWINDOW, FALSE, 0); + } + + init_event_queue(ctx); + + ctx->hMainWnd = CreateWindowEx (0,"freesci.WndClass","FreeSCI", + g_bFullScreen ? WS_POPUP : (WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU), + 0,0,rc.right-rc.left,rc.bottom-rc.top,NULL,NULL,NULL,NULL); + + SetWindowLong(ctx->hMainWnd,0,(long) drv); + + hr = DirectDrawCreate(NULL,&(ctx->pDD),NULL); + if ( FAILED(hr)) + { + DestroyWindow(ctx->hMainWnd); + free(ctx); + return GFX_FATAL; + } + + + if(g_bFullScreen) + { + // fulscreen mode, change to the desired mode + hr = ctx->pDD->SetCooperativeLevel ( ctx->hMainWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + hr = ctx->pDD->SetDisplayMode ( xres*320, yres*200, dd_bpp); + } + else + { + // windowed mode, accept whatever mode is current + hr = ctx->pDD->SetCooperativeLevel (ctx->hMainWnd, DDSCL_NORMAL); + } + + drv->state = ctx; + + ShowWindow(ctx->hMainWnd,SW_NORMAL); + UpdateWindow(ctx->hMainWnd); + + pnt.x = 0; pnt.y = 0; + ClientToScreen (ctx->hMainWnd, &pnt); + ctx->win_xpos = pnt.x; + ctx->win_ypos = pnt.y; + + // create surface - primary + memset(&dd_desc,0,sizeof(dd_desc)); + dd_desc.dwSize = sizeof(dd_desc); + dd_desc.dwFlags = DDSD_CAPS; + dd_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + hr = ctx->pDD->CreateSurface(&dd_desc,&ctx->pPrimary,NULL); + if(FAILED(hr)) + { + if(g_bFullScreen) + { + hr = ctx->pDD->RestoreDisplayMode (); + hr = ctx->pDD->SetCooperativeLevel (ctx->hMainWnd, DDSCL_NORMAL); + } + ctx->pDD->Release(); + free(ctx); + return GFX_FATAL; + } + + // get the current mode; in windowed mode it can be different than the one we want + memset(&dd_desc,0,sizeof(dd_desc)); + dd_desc.dwSize = sizeof(dd_desc); + hr = ctx->pDD->GetDisplayMode(&dd_desc); + + r_sh = ShiftCount(dd_desc.ddpfPixelFormat.dwRBitMask); + g_sh = ShiftCount(dd_desc.ddpfPixelFormat.dwGBitMask); + b_sh = ShiftCount(dd_desc.ddpfPixelFormat.dwBBitMask); + dd_bpp2=0; + if(dd_desc.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED1) + dd_bpp2=2; + if(dd_desc.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED2) + dd_bpp2=4; + if(dd_desc.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED4) + dd_bpp2=16; + if(dd_desc.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8) + dd_bpp2=256; + + drv->mode = gfx_new_mode(xres,yres,dd_desc.ddpfPixelFormat.dwRGBBitCount/8, + dd_desc.ddpfPixelFormat.dwRBitMask,dd_desc.ddpfPixelFormat.dwGBitMask,dd_desc.ddpfPixelFormat.dwBBitMask,0, + r_sh,g_sh,b_sh,0, dd_bpp2, 0); + + tbytes = 320*xres*200*yres* (dd_desc.ddpfPixelFormat.dwRGBBitCount/8); + + // create a secondary surfaces + memset (&ddsd, 0, sizeof (DDSURFACEDESC)); + ddsd.dwSize = sizeof (DDSURFACEDESC); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + ddsd.dwWidth = 320 * drv->mode->xfact; + ddsd.dwHeight = 200 * drv->mode->yfact; + hr = ctx->pDD->CreateSurface(&ddsd,&ctx->pBack,NULL); + if(FAILED(hr)) + { + return GFX_FATAL; + } + memset (&ddsd, 0, sizeof (DDSURFACEDESC)); + ddsd.dwSize = sizeof (DDSURFACEDESC); + hr = ctx->pBack->Lock(NULL,&ddsd,DDLOCK_WAIT,NULL); + if(!FAILED(hr)) + { + memset(ddsd.lpSurface,0,tbytes); + hr = ctx->pBack->Unlock(NULL); + } + + memset (&ddsd, 0, sizeof (DDSURFACEDESC)); + ddsd.dwSize = sizeof (DDSURFACEDESC); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + ddsd.dwWidth = 320 * drv->mode->xfact; + ddsd.dwHeight = 200 * drv->mode->yfact; + hr = ctx->pDD->CreateSurface(&ddsd,&ctx->pStatic,NULL); + if(FAILED(hr)) + { + return GFX_FATAL; + } + memset (&ddsd, 0, sizeof (DDSURFACEDESC)); + ddsd.dwSize = sizeof (DDSURFACEDESC); + hr = ctx->pStatic->Lock(NULL,&ddsd,DDLOCK_WAIT,NULL); + if(!FAILED(hr)) + { + memset(ddsd.lpSurface,0,tbytes); + hr = ctx->pStatic->Unlock(NULL); + } + + // if not in full-screen mode, set the clipper + if (!g_bFullScreen) + { + hr = ctx->pDD->CreateClipper (0, &ctx->pClipper, NULL); + hr = ctx->pClipper->SetHWnd (0, ctx->hMainWnd); + hr = ctx->pPrimary->SetClipper (ctx->pClipper); + } + + ctx->priority_maps[DD_BUFFER_BACK] = + gfx_pixmap_alloc_index_data(gfx_new_pixmap(320 * xres, 200 * yres, GFX_RESID_NONE, 0, 0)); + ctx->priority_maps[DD_BUFFER_STATIC] = + gfx_pixmap_alloc_index_data(gfx_new_pixmap(320 * xres, 200 * yres, GFX_RESID_NONE, 0, 0)); + + ctx->priority_maps[DD_BUFFER_BACK]->flags |= GFX_PIXMAP_FLAG_SCALED_INDEX; + ctx->priority_maps[DD_BUFFER_STATIC]->flags |= GFX_PIXMAP_FLAG_SCALED_INDEX; + +/* + if (_open_meta_visuals(drv)) { + free(meta); + gfx_free_pixmap(drv, meta->priority_maps[GGI_BUFFER_BACK]); + gfx_free_pixmap(drv, meta->priority_maps[GGI_BUFFER_STATIC]); + ggiClose(meta->vis); + ggiExit(); + return GFX_ERROR; + } + + if (frames < 2) { + meta->alt_back_buffer = sci_malloc(bpp * 320 * 200 * xres * yres); + meta->back_vis = ggiOpen("memory:pointer", meta->alt_back_buffer, NULL); + if (ggiSetSimpleMode(meta->back_vis, xres * 320, yres * 200, 1, GT_8BIT)) { + sciprintf("GFXGGI: Warning: Setting mode for memory visual failed\n"); + } + } else meta->alt_back_buffer = NULL; + + if (frames < 3) { + meta->static_buffer = sci_malloc(bpp * 320 * 200 * xres * yres); + meta->static_vis = ggiOpen("memory:pointer", meta->static_buffer, NULL); + if (ggiSetSimpleMode(meta->static_vis, xres * 320, yres * 200, 1, GT_8BIT)) { + sciprintf("GFXGGI: Warning: Setting mode for memory visual #2 failed\n"); + } + } else meta->static_buffer = NULL; + + init_input_ggi(); +*/ + return GFX_OK; +} + +int +dd_init(gfx_driver_t *drv) +{ + return dd_init_specific(drv,1,1,1); +} + +void +dd_exit(gfx_driver_t *drv) +{ + HRESULT hr; + gfx_dd_struct_t *ctx; + + if(drv->state == NULL) + return; + + ctx = (gfx_dd_struct_t *) drv->state; + + if(g_bFullScreen) + { + hr = ctx->pDD->RestoreDisplayMode (); + hr = ctx->pDD->SetCooperativeLevel (ctx->hMainWnd, DDSCL_NORMAL); + } + + free_event_queue(ctx); + + gfx_free_pixmap(drv, ctx->priority_maps[0]); + gfx_free_pixmap(drv, ctx->priority_maps[1]); + + if(ctx->hMainWnd) + { + DestroyWindow(ctx->hMainWnd); + ctx->hMainWnd = NULL; + } + + if(ctx->pBack) + { + ctx->pBack->Release(); + ctx->pBack=NULL; + } + if(ctx->pStatic) + { + ctx->pStatic->Release(); + ctx->pStatic=NULL; + } + if(ctx->pPrimary) + { + ctx->pPrimary->Release(); + ctx->pPrimary=NULL; + } + if(ctx->pClipper) + { + ctx->pClipper->Release(); + ctx->pClipper=NULL; + } + if(ctx->pDD) + { + ctx->pDD->Release(); + ctx->pDD = NULL; + } + + drv->state = NULL; + + free(ctx); +} + +int +dd_draw_line(gfx_driver_t *drv, + point_t start, point_t end, + gfx_color_t color, + gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + rect_t line = gfx_rect(start.x, start.y, + end.x - start.x, end.y - start.y); + HRESULT hr; + DDSURFACEDESC ddsc; + gfx_dd_struct_t *ctx; + + if(drv->state == NULL) + return GFX_ERROR; + + ctx = (gfx_dd_struct_t *) drv->state; + + memset(&ddsc,0,sizeof(ddsc)); + ddsc.dwSize = sizeof(ddsc); + hr = ctx->pBack->Lock(NULL,&ddsc,DDLOCK_WAIT,NULL); + if(FAILED(hr)) + { + return GFX_ERROR; + } + + _dd_draw_line(drv->mode, start, end, (byte *) ddsc.lpSurface, ddsc.lPitch,color); + +// gfx_crossblit_pixmap(drv->mode, pxm, priority, src, dest, (byte *) ddsc.lpSurface, +// ddsc.lPitch,pri_map, drv->mode->xfact * 320, 1); + + hr = ctx->pBack->Unlock(NULL); + return GFX_OK; +} + +int +dd_draw_filled_rect(gfx_driver_t *drv, rect_t box, gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + HRESULT hr; + RECT rcDest; + DDBLTFX ddblt; + gfx_dd_struct_t *ctx; + byte *pri; + int i; + + if(drv->state == NULL) + return GFX_ERROR; + + ctx = (gfx_dd_struct_t *) drv->state; + + rcDest.left = box.x; + rcDest.top = box.y; + rcDest.right = box.x+box.xl; + rcDest.bottom = box.y+box.yl; + + if (color1.mask & GFX_MASK_VISUAL) + { + memset(&ddblt,0,sizeof(ddblt)); + ddblt.dwSize = sizeof(ddblt); + if(drv->mode->palette!=NULL) + ddblt.dwFillColor = color1.visual.global_index; + else + ddblt.dwFillColor = MakeRGB(drv->mode,color1.visual.r,color1.visual.g,color1.visual.b); + hr = ctx->pBack->Blt(&rcDest,NULL,NULL,DDBLT_COLORFILL,&ddblt); + } + + if (color1.mask & GFX_MASK_PRIORITY) + { + pri = ctx->priority_maps[DD_BUFFER_BACK]->index_data + box.x + box.y*(drv->mode->xfact * 320); + for(i=0; imode->xfact * 320; + } + } + + return GFX_OK; +} + +int +dd_draw_pixmap(gfx_driver_t *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + HRESULT hr; + gfx_dd_struct_t *ctx; + LPDIRECTDRAWSURFACE dst; + DDSURFACEDESC ddsc; + byte *pri_map = NULL; + + if(drv->state == NULL) + return GFX_ERROR; + + ctx = (gfx_dd_struct_t *) drv->state; + + if (src.xl != dest.xl || src.yl != dest.yl) + { + GFXERROR("Attempt to draw scaled view- not supported by this driver!\n"); + return GFX_FATAL; /* Scaling not supported! */ + } + + switch(buffer) + { + case GFX_BUFFER_FRONT: + GFXERROR("Attempt to draw pixmap to front buffer\n"); + return GFX_ERROR; + case GFX_BUFFER_BACK: + dst = ctx->pBack; + pri_map = ctx->priority_maps[DD_BUFFER_BACK]->index_data; + break; + case GFX_BUFFER_STATIC: + dst = ctx->pStatic; + pri_map = ctx->priority_maps[DD_BUFFER_STATIC]->index_data; + break; + default: + GFXERROR("Unexpected buffer ID %d\n", buffer); + return GFX_ERROR; + } + + memset(&ddsc,0,sizeof(ddsc)); + ddsc.dwSize = sizeof(ddsc); + hr = dst->Lock(NULL,&ddsc,DDLOCK_WAIT,NULL); + if(FAILED(hr)) + { + return GFX_ERROR; + } + + gfx_crossblit_pixmap(drv->mode, pxm, priority, src, dest, (byte *) ddsc.lpSurface, + ddsc.lPitch,pri_map, drv->mode->xfact * 320, 1, 0); + + hr = dst->Unlock(NULL); + + return GFX_OK; +} + +int +dd_grab_pixmap(gfx_driver_t *drv, rect_t src, gfx_pixmap_t *pxm, gfx_map_mask_t map) +{ + HRESULT hr; + gfx_dd_struct_t *ctx; + DDSURFACEDESC ddsc; + int i,x,y,xlb,bpp; + BYTE *s,*d; + + if(drv->state == NULL) + return GFX_ERROR; + + ctx = (gfx_dd_struct_t *) drv->state; + + if (src.x < 0 || src.y < 0) + { + GFXERROR("Attempt to grab pixmap from invalid coordinates (%d,%d)\n", src.x, src.y); + return GFX_ERROR; + } + + if (!pxm->data) + { + GFXERROR("Attempt to grab pixmap to unallocated memory\n"); + return GFX_ERROR; + } + + pxm->xl = src.xl; + pxm->yl = src.yl; + + memset(&ddsc,0,sizeof(ddsc)); + ddsc.dwSize = sizeof(ddsc); + hr = ctx->pBack->Lock(NULL,&ddsc,DDLOCK_WAIT,NULL); + if(FAILED(hr)) + return GFX_ERROR; + + bpp = ddsc.ddpfPixelFormat.dwRGBBitCount / 8; + x = src.x*drv->mode->xfact; + y = src.y*drv->mode->yfact; +/* + if(!g_bFullScreen) + { + x += ctx->win_xpos; + y += ctx->win_ypos; + } +*/ + s = (BYTE *) ddsc.lpSurface + x*bpp + y*ddsc.lPitch; + d = pxm->data; + xlb = src.xl * bpp; + + for(i=0; ipBack->Unlock(NULL); + + return GFX_OK; +} + +static int +dd_update(gfx_driver_t *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + gfx_dd_struct_t *ctx; + RECT rcDst,rcSrc; + HRESULT hr; + LPDIRECTDRAWSURFACE p_dst,p_src; + + if(drv->state == NULL) + return GFX_ERROR; + + ctx = (gfx_dd_struct_t *) drv->state; + + rcSrc.left = src.x; + rcSrc.right = src.x + src.xl; + rcSrc.top = src.y; + rcSrc.bottom = src.y + src.yl; + + rcDst.left = dest.x; + rcDst.right = dest.x + src.xl; + rcDst.top = dest.y; + rcDst.bottom = dest.y + src.yl; + + switch(buffer) + { + case GFX_BUFFER_FRONT: + rcDst.left += ctx->win_xpos; + rcDst.right += ctx->win_xpos; + rcDst.top += ctx->win_ypos; + rcDst.bottom += ctx->win_ypos; + p_dst = ctx->pPrimary; + p_src = ctx->pBack; + break; + case GFX_BUFFER_BACK: + p_dst = ctx->pBack; + p_src = ctx->pStatic; + break; + default: + return GFX_ERROR; + } + + hr = p_dst->Blt(&rcDst,p_src,&rcSrc,DDBLT_WAIT,NULL); + if(FAILED(hr)) + return GFX_ERROR; + + return GFX_OK; +} + +static int +dd_set_static_buffer(gfx_driver_t *drv, gfx_pixmap_t *pic, gfx_pixmap_t *priority) +{ + gfx_dd_struct_t *ctx; + DDSURFACEDESC ddsc; + HRESULT hr; + int i,xs; + byte *s,*d; +// ggi_visual_t vis = get_writeable_static_visual(drv); + + if(drv->state == NULL) + return GFX_ERROR; + + ctx = (gfx_dd_struct_t *) drv->state; + + /* First, check if the priority map is sane */ + if (priority->index_xl != ctx->priority_maps[DD_BUFFER_STATIC]->index_xl + || priority->index_yl != ctx->priority_maps[DD_BUFFER_STATIC]->index_yl) + { + GFXERROR("Invalid priority map: (%dx%d) vs expected (%dx%d)\n", + priority->index_xl, priority->index_yl, + ctx->priority_maps[DD_BUFFER_STATIC]->index_xl, + ctx->priority_maps[DD_BUFFER_STATIC]->index_yl); + return GFX_ERROR; + } + +// ggiPutBox(vis, 0, 0, pic->xl, pic->yl, pic->data); + memset(&ddsc,0,sizeof(ddsc)); + ddsc.dwSize = sizeof(ddsc); + hr = ctx->pStatic->Lock(NULL,&ddsc,DDLOCK_WAIT,NULL); + if(FAILED(hr)) + { + return GFX_ERROR; + } + + s = pic->data; + d = (byte *) ddsc.lpSurface; + xs = drv->mode->bytespp * pic->xl; + + for(i=0; iyl; i++) + { + memcpy(d,s,xs); + s+= xs; + d+= ddsc.lPitch; + } + + hr = ctx->pStatic->Unlock(NULL); + + memcpy(ctx->priority_maps[DD_BUFFER_STATIC]->index_data, priority->index_data, + priority->index_xl * priority->index_yl); + + return GFX_OK; +} + +static int +dd_set_palette(gfx_driver_t *drv, int index, byte red, byte green, byte blue) +{ + //!! implement me !! + return GFX_ERROR; +} + +// ------------------------------------- non-drawing functions ------------------------------------ +sci_event_t get_queue_event(gfx_dd_struct_t *ctx); + +static int process_messages(void) +{ + MSG msg; + + while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) + { + if (!GetMessage (&msg, NULL, 0, 0)) + return 0; + TranslateMessage (&msg); + DispatchMessage(&msg); + } + return 1; +} + +static sci_event_t +dd_get_event(gfx_driver_t *drv) +{ + gfx_dd_struct_t *ctx; + + process_messages(); + + assert(drv->state!=NULL); + + ctx = (gfx_dd_struct_t *) drv->state; + + return get_queue_event(ctx); +} + +static void +MsgWait (long WaitTime) +{ + DWORD dwRet=0; + long dwWait; + DWORD StartTime=timeGetTime(); + + dwWait = WaitTime; + + if (!process_messages()) + return; + + if (dwWait <= 0) + return; + do + { + dwWait=WaitTime-(timeGetTime()-StartTime); + if (dwWait <= 0) + break; + + dwRet=MsgWaitForMultipleObjects (0, NULL, FALSE, dwWait, QS_ALLINPUT); + + if(dwRet == WAIT_TIMEOUT) + return; + + if (!process_messages()) + return; + + } while(1); +} + +int +dd_usleep(gfx_driver_t* drv, long usec) +{ + if (usec < 1000) + Sleep(usec / 1000); + else + MsgWait (usec / 1000); + + return GFX_OK; +} +/* --------------------------------------------------------------- */ + +static void init_event_queue(gfx_dd_struct_t *ctx) +{ + ctx->queue_first = 0; + ctx->queue_last = 0; + ctx->queue_size = 256; + ctx->event_queue = (sci_event_t *) sci_malloc (ctx->queue_size * sizeof (sci_event_t)); +} + +static void free_event_queue(gfx_dd_struct_t *ctx) +{ + if (ctx->event_queue) sci_free(ctx->event_queue); + ctx->queue_size = 0; + ctx->queue_first =0; + ctx->queue_last =0; +} + +static void add_queue_event(gfx_dd_struct_t *ctx,int type, int data, int buckybits) +{ + if ((ctx->queue_last+1) % ctx->queue_size == ctx->queue_first) + { + /* Reallocate queue */ + int i, event_count; + sci_event_t *new_queue; + + new_queue = (sci_event_t *) sci_malloc (ctx->queue_size * 2 * sizeof (sci_event_t)); + event_count = (ctx->queue_last - ctx->queue_first) % ctx->queue_size; + for (i=0; ievent_queue [(ctx->queue_first+i) % ctx->queue_size]; + free (ctx->event_queue); + ctx->event_queue = new_queue; + ctx->queue_size *= 2; + ctx->queue_first = 0; + ctx->queue_last = event_count; + } + + ctx->event_queue [ctx->queue_last].data = data; + ctx->event_queue [ctx->queue_last].type = type; + ctx->event_queue [ctx->queue_last++].buckybits = buckybits; + if (ctx->queue_last == ctx->queue_size) + ctx->queue_last=0; +} + +static void add_mouse_event (gfx_dd_struct_t *ctx,int type, int data, WPARAM wParam) +{ + int buckybits = 0; + + if (wParam & MK_SHIFT) + buckybits |= SCI_EVM_LSHIFT | SCI_EVM_RSHIFT; + if (wParam & MK_CONTROL) + buckybits |= SCI_EVM_CTRL; + + add_queue_event (ctx,type, data, buckybits); +} + +static void add_key_event (gfx_dd_struct_t *ctx,int data) +{ + int buckybits = 0; + + /* FIXME: If anyone cares, on Windows NT we can distinguish left and right shift */ + if (GetAsyncKeyState (VK_SHIFT)) + buckybits |= SCI_EVM_LSHIFT | SCI_EVM_RSHIFT; + if (GetAsyncKeyState (VK_CONTROL)) + buckybits |= SCI_EVM_CTRL; + if (GetAsyncKeyState (VK_MENU)) + buckybits |= SCI_EVM_ALT; + if (GetKeyState (VK_CAPITAL) & 1) + buckybits |= SCI_EVM_CAPSLOCK; + + add_queue_event (ctx,SCI_EVT_KEYBOARD, data, buckybits); +} + +sci_event_t get_queue_event(gfx_dd_struct_t *ctx) +{ + if (ctx->queue_first == ctx->queue_size) + ctx->queue_first = 0; + + if (ctx->queue_first == ctx->queue_last) + { + sci_event_t noevent; + noevent.data = 0; + noevent.type = SCI_EVT_NONE; + noevent.buckybits = 0; + return noevent; + } + else + return ctx->event_queue [ctx->queue_first++]; +} + +#define MAP_KEY(x,y) case x: add_key_event (ctx, y); break + +static long FAR PASCAL WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + HRESULT hr; + gfx_dd_struct_t *ctx; + gfx_driver_t* drv; + + drv = (gfx_driver_t *) GetWindowLong(hWnd,0); + if(drv == NULL) + return DefWindowProc(hWnd,message,wParam,lParam); + ctx = (gfx_dd_struct_t *) drv->state; + if(ctx == NULL) + return DefWindowProc(hWnd,message,wParam,lParam); + + switch(message) + { + case WM_SIZE: + case WM_MOVE: + { + POINT pnt; + pnt.x = 0; pnt.y = 0; + ClientToScreen (hWnd, &pnt); + ctx->win_xpos = pnt.x; + ctx->win_ypos = pnt.y; + } + break; + case WM_PAINT: + { + RECT rcSrc,rcDst; + if (ctx->pPrimary!=NULL && ctx->pBack!=NULL && GetUpdateRect (hWnd, &rcSrc, FALSE)) + { + if(rcSrc.right>320) + rcSrc.right=320; + if(rcSrc.bottom>200) + rcSrc.bottom=200; + rcDst.left = rcSrc.left + ctx->win_xpos; + rcDst.right = rcSrc.right + ctx->win_xpos; + rcDst.top = rcSrc.top + ctx->win_ypos; + rcDst.bottom = rcSrc.bottom + ctx->win_ypos; + hr = ctx->pPrimary->Blt(&rcDst,ctx->pBack,&rcSrc,DDBLT_WAIT,NULL); + ValidateRect(hWnd,&rcSrc); + } + } + break; + case WM_SETCURSOR: + if(IsIconic(hWnd) || ctx->bShowMouse) + { + SetCursor(LoadCursor (NULL, IDC_ARROW)); + } + else + { + SetCursor(NULL); + } + return TRUE; + case WM_NCMOUSEMOVE: + ctx->bShowMouse = TRUE; + break; + case WM_MOUSEMOVE: + ctx->bShowMouse = FALSE; + if( ((lParam & 0xFFFF) >= 320*drv->mode->xfact) || ((lParam >> 16) >= 200*drv->mode->yfact)) + break; + drv->pointer_x = lParam & 0xFFFF; + drv->pointer_y = lParam >> 16; + break; + + case WM_LBUTTONDOWN: add_mouse_event (ctx, SCI_EVT_MOUSE_PRESS, 1, wParam); break; + case WM_RBUTTONDOWN: add_mouse_event (ctx, SCI_EVT_MOUSE_PRESS, 2, wParam); break; + case WM_MBUTTONDOWN: add_mouse_event (ctx, SCI_EVT_MOUSE_PRESS, 3, wParam); break; + case WM_LBUTTONUP: add_mouse_event (ctx, SCI_EVT_MOUSE_RELEASE, 1, wParam); break; + case WM_RBUTTONUP: add_mouse_event (ctx, SCI_EVT_MOUSE_RELEASE, 2, wParam); break; + case WM_MBUTTONUP: add_mouse_event (ctx, SCI_EVT_MOUSE_RELEASE, 3, wParam); break; + + case WM_KEYDOWN: + switch (wParam) + { + MAP_KEY (VK_ESCAPE, SCI_K_ESC); + MAP_KEY (VK_END, SCI_K_END); + MAP_KEY (VK_DOWN, SCI_K_DOWN); + MAP_KEY (VK_NEXT, SCI_K_PGDOWN); + MAP_KEY (VK_LEFT, SCI_K_LEFT); + MAP_KEY (VK_RIGHT, SCI_K_RIGHT); + MAP_KEY (VK_HOME, SCI_K_HOME); + MAP_KEY (VK_UP, SCI_K_UP); + MAP_KEY (VK_PRIOR, SCI_K_PGUP); + MAP_KEY (VK_INSERT, SCI_K_INSERT); + MAP_KEY (VK_DELETE, SCI_K_DELETE); + MAP_KEY (VK_BACK, SCI_K_BACKSPACE); + MAP_KEY (VK_TAB, '\t'); + MAP_KEY (VK_RETURN, '\r'); + + default: + if (wParam >= VK_F1 && wParam <= VK_F10) + add_key_event (ctx, wParam - VK_F1 + SCI_K_F1); + else if (wParam >= 'A' && wParam <= 'Z') + add_key_event (ctx, wParam - 'A' + 97); + else if (wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9) + { + if (GetKeyState (VK_NUMLOCK) & 1) + add_key_event (ctx, wParam - VK_NUMPAD0 + '0'); + else + switch (wParam) + { + MAP_KEY (VK_NUMPAD0, SCI_K_INSERT); + MAP_KEY (VK_NUMPAD1, SCI_K_END); + MAP_KEY (VK_NUMPAD2, SCI_K_DOWN); + MAP_KEY (VK_NUMPAD3, SCI_K_PGDOWN); + MAP_KEY (VK_NUMPAD4, SCI_K_LEFT); + MAP_KEY (VK_NUMPAD6, SCI_K_RIGHT); + MAP_KEY (VK_NUMPAD7, SCI_K_HOME); + MAP_KEY (VK_NUMPAD8, SCI_K_UP); + MAP_KEY (VK_NUMPAD9, SCI_K_PGUP); + } + } + else if (wParam == 0xC0) /* tilde key - used for invoking console */ + add_key_event (ctx, '`'); + else + add_key_event (ctx, wParam); + break; + } + } + return DefWindowProc(hWnd,message,wParam,lParam); +} + +/* --------------------------------------------------------------- */ +extern "C" +gfx_driver_t gfx_driver_dd = { + "ddraw", + DD_DRIVER_VERSION, + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, + 0,0, + 0, + GFX_DEBUG_POINTER | GFX_DEBUG_UPDATES | GFX_DEBUG_PIXMAPS | GFX_DEBUG_BASIC, + dd_set_param, + dd_init_specific, + dd_init, + dd_exit, + dd_draw_line, + dd_draw_filled_rect, + NULL, + NULL, + dd_draw_pixmap, + dd_grab_pixmap, + dd_update, + dd_set_static_buffer, + NULL, + dd_set_palette, + dd_get_event, + dd_usleep +}; + +#endif // HAVE_DDRAW diff --git a/engines/sci/gfx/drivers/dd_driver.h b/engines/sci/gfx/drivers/dd_driver.h new file mode 100644 index 0000000000..209fe51569 --- /dev/null +++ b/engines/sci/gfx/drivers/dd_driver.h @@ -0,0 +1,2 @@ + +void _dd_draw_line(gfx_mode_t * mode, rect_t line, byte * dest, int linewidth,gfx_color_t color); diff --git a/engines/sci/gfx/drivers/dd_driver_line.cpp b/engines/sci/gfx/drivers/dd_driver_line.cpp new file mode 100644 index 0000000000..e83b368a5e --- /dev/null +++ b/engines/sci/gfx/drivers/dd_driver_line.cpp @@ -0,0 +1,163 @@ +#ifndef __cplusplus +#error NOTE: This file MUST be compiled as C++. In Visual C++, use the /Tp command line option. +#endif + +extern "C" { +#include +#include +}; + +#define LINEMACRO_1(startx, starty, deltalinear, deltanonlinear, linearvar, nonlinearvar, \ + linearend, nonlinearstart, linearmod, nonlinearmod) \ + x = (startx); y = (starty); \ + incrNE = ((deltalinear) > 0)? (deltalinear) : -(deltalinear); \ + incrNE <<= 1; \ + deltanonlinear <<= 1; \ + incrE = ((deltanonlinear) > 0) ? -(deltanonlinear) : (deltanonlinear); \ + d = nonlinearstart-1; \ + while (linearvar != (linearend)) { \ + buffer[linewidth * y + x] = color; \ + linearvar += linearmod; \ + if ((d+=incrE) < 0) { \ + d += incrNE; \ + nonlinearvar += nonlinearmod; \ + }; \ + }; \ + buffer[linewidth * y + x] = color; + +#define LINEMACRO_N(startx, starty, deltalinear, deltanonlinear, linearvar, nonlinearvar, \ + linearend, nonlinearstart, linearmod, nonlinearmod) \ + x = (startx); y = (starty); \ + incrNE = ((deltalinear) > 0)? (deltalinear) : -(deltalinear); \ + incrNE <<= 1; \ + deltanonlinear <<= 1; \ + incrE = ((deltanonlinear) > 0) ? -(deltanonlinear) : (deltanonlinear); \ + d = nonlinearstart-1; \ + while (linearvar != (linearend)) { \ + memcpy(&buffer[linewidth * y + bpp*x],&color,bpp); \ + linearvar += linearmod; \ + if ((d+=incrE) < 0) { \ + d += incrNE; \ + nonlinearvar += nonlinearmod; \ + }; \ + }; \ + memcpy(&buffer[linewidth * y + x],&color,bpp); + +void _dd_draw_line_buffer_1(byte *buffer, int linewidth, rect_t line, int color) +{ + /*void dither_line(picture_t buffers, int curx, int cury, short x1, short y1, + int col1, int col2, int priority, int special, char drawenable)*/ + + int dx, dy, incrE, incrNE, d, finalx, finaly; + int x = line.x; + int y = line.y; + dx = line.xl; + dy = line.yl; + finalx = x + dx; + finaly = y + dy; + + dx = abs(dx); + dy = abs(dy); + + if (dx > dy) { + if (finalx < x) { + if (finaly < y) { /* llu == left-left-up */ + LINEMACRO_1(x, y, dx, dy, x, y, finalx, dx, -1, -1); + } else { /* lld */ + LINEMACRO_1(x, y, dx, dy, x, y, finalx, dx, -1, 1); + } + } else { /* x1 >= x */ + if (finaly < y) { /* rru */ + LINEMACRO_1(x, y, dx, dy, x, y, finalx, dx, 1, -1); + } else { /* rrd */ + LINEMACRO_1(x, y, dx, dy, x, y, finalx, dx, 1, 1); + } + } + } else { /* dx <= dy */ + if (finaly < y) { + if (finalx < x) { /* luu */ + LINEMACRO_1(x, y, dy, dx, y, x, finaly, dy, -1, -1); + } else { /* ruu */ + LINEMACRO_1(x, y, dy, dx, y, x, finaly, dy, -1, 1); + } + } else { /* y1 >= y */ + if (finalx < x) { /* ldd */ + LINEMACRO_1(x, y, dy, dx, y, x, finaly, dy, 1, -1); + } else { /* rdd */ + LINEMACRO_1(x, y, dy, dx, y, x, finaly, dy, 1, 1); + } + } + } +} + +void _dd_draw_line_buffer_n(int bpp,byte *buffer, int linewidth, rect_t line, byte * color) +{ + /*void dither_line(picture_t buffers, int curx, int cury, short x1, short y1, + int col1, int col2, int priority, int special, char drawenable)*/ + + int dx, dy, incrE, incrNE, d, finalx, finaly; + int x = line.x; + int y = line.y; + dx = line.xl; + dy = line.yl; + finalx = x + dx; + finaly = y + dy; + + dx = abs(dx); + dy = abs(dy); + + if (dx > dy) { + if (finalx < x) { + if (finaly < y) { /* llu == left-left-up */ + LINEMACRO_N(x, y, dx, dy, x, y, finalx, dx, -1, -1); + } else { /* lld */ + LINEMACRO_N(x, y, dx, dy, x, y, finalx, dx, -1, 1); + } + } else { /* x1 >= x */ + if (finaly < y) { /* rru */ + LINEMACRO_N(x, y, dx, dy, x, y, finalx, dx, 1, -1); + } else { /* rrd */ + LINEMACRO_N(x, y, dx, dy, x, y, finalx, dx, 1, 1); + } + } + } else { /* dx <= dy */ + if (finaly < y) { + if (finalx < x) { /* luu */ + LINEMACRO_N(x, y, dy, dx, y, x, finaly, dy, -1, -1); + } else { /* ruu */ + LINEMACRO_N(x, y, dy, dx, y, x, finaly, dy, -1, 1); + } + } else { /* y1 >= y */ + if (finalx < x) { /* ldd */ + LINEMACRO_N(x, y, dy, dx, y, x, finaly, dy, 1, -1); + } else { /* rdd */ + LINEMACRO_N(x, y, dy, dx, y, x, finaly, dy, 1, 1); + } + } + } +} + +void _dd_draw_line(gfx_mode_t * mode, rect_t line, byte * dest, int linewidth,gfx_color_t color) +{ + byte ca[4]; + unsigned short co1; + + switch(mode->bytespp) + { + case 1: + _dd_draw_line_buffer_1(dest,linewidth,line,color.visual.global_index); + break; + case 2: + co1 = (color.visual.r << mode->red_shift) & mode->red_mask; + co1 |= (color.visual.g << mode->green_shift) & mode->green_mask; + co1 |= (color.visual.b << mode->blue_shift) & mode->blue_mask; + _dd_draw_line_buffer_n(2,dest,linewidth,line, (byte *) &co1); + break; + case 3: + ca[mode->red_shift/8]=color.visual.r; + ca[mode->green_shift/8]=color.visual.g; + ca[mode->blue_shift/8]=color.visual.b; + _dd_draw_line_buffer_n(3,dest,linewidth,line, ca); + break; + } +} diff --git a/engines/sci/gfx/drivers/directfb_driver.c b/engines/sci/gfx/drivers/directfb_driver.c new file mode 100644 index 0000000000..634b2b11df --- /dev/null +++ b/engines/sci/gfx/drivers/directfb_driver.c @@ -0,0 +1,1033 @@ +/*************************************************************************** + directfb_driver.c Copyright (C) 2002 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* FreeSCI 0.3.1+ graphics driver module for libdirectfb */ + + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_DIRECTFB + +#include +#include +#include +#include + +#define SCIDFB_DRIVER_VERSION "0.4" + +typedef struct _event_queue_struct { + sci_event_t evt; + struct _event_queue_struct *next; +} event_queue_t; + +/* +** For this driver to work to the fullest of its capabilities, the user running it +** must have rw permissions on the following: +** /dev/tty0 (including VT_ACTIVATE perms) +** /dev/tty7 +** /dev/fb0 +** /dev/psaux (if using a PS/2 mouse) +** +** VT_ACTIVATE permissions are, AFAIK, only available to root ATM :-( +*/ + +#define SCIDFB_CAP_ALPHA (1 << 0) /* Do we have an Alpha channel? */ + +#define SCIDFB_MAY_ALPHA (scidfb_internal_caps & SCIDFB_CAP_ALPHA) + +/* Global data */ + +static IDirectFB *scidfb_framebuffer; /* The global framebuffer */ +static IDirectFBDisplayLayer *scidfb_layer; /* The layer we use for drawing */ +static IDirectFBSurface *scidfb_visual; /* Static visual buffer */ +static IDirectFBSurface *scidfb_static_visual; /* Static visual buffer */ +static gfx_pixmap_t *scidfb_static_priority = NULL; +static gfx_pixmap_t *scidfb_back_priority = NULL; +static int scidfb_internal_caps = 0; /* Certain internal capabilities available to us */ + +/* Input structures */ +static IDirectFBEventBuffer *scidfb_input_buffer = NULL; +static event_queue_t *scidfb_event_queue = NULL; +static unsigned int scidfb_buckybits = SCI_EVM_INSERT; + + +/*#define LIST_ALL_DFB_MODES */ /* Uncomment for minimal debugging */ + +#define SCIDFB_CHECKED(f) \ + { \ + DFBResult _r = (f); \ + if (_r != DFB_OK) { \ + scidfb_handle_error(_r, __FILE__, __LINE__); \ + return GFX_ERROR; \ + } \ + } + +#define SCIGFX_CHECKED(f) \ + { \ + DFBResult _r = (f); \ + if (_r != GFX_OK) { \ + return _r; \ + } \ + } + + +static void +scidfb_handle_error(DFBResult errc, char *file, int line) +{ + const char *err = DirectFBErrorString(errc); + + GFXERROR("DFB-GFX, %s L%d: %s\n", file, line, err); +} + + +typedef struct { + int x, y, bpp; +} scidfb_mode_t; + + +static DFBEnumerationResult +scidfb_mode_callback(unsigned int width, unsigned int height, unsigned int bpp, void *conf) +{ + scidfb_mode_t *aim = (scidfb_mode_t *) conf; + scidfb_mode_t *best = aim + 1; + + if ((width >= aim->x && height >= aim->y && bpp >= aim->bpp) + /* Sufficient in all respects... */ + && ((best->bpp == 0) /* First mode that matched */ + || (width < best->x && height <= best->y) /* Improvement on x */ + || (width <= best->x && height < best->y) /* Improvement on y */ + || (width <= best->x && height <= best->y && bpp < best->bpp) + /* Improvement on bpp */ + )) { + best->x = width; + best->y = height; + best->bpp = bpp; + } + +#ifdef LIST_ALL_DFB_MODES + GFXERROR("DFB-GFX: Supports %dx%d at %d bpp\n", width, height, bpp); + return DFENUM_OK; +#endif + + if (aim->x == best->x + && aim->y == best->y + && aim->bpp == best->bpp) + return DFENUM_CANCEL; /* We have what we were looking for */ + + return DFENUM_OK; /* Continue searching */ +} + + +static DFBEnumerationResult +scidfb_layer_callback(unsigned int layer_id, DFBDisplayLayerCapabilities caps, void *resultp) +{ + unsigned int *results = (unsigned int *) resultp; + + if (!(caps & DLCAPS_SURFACE)) + return DFENUM_OK; /* We need a surface */ + + if (!results[1]) + results[0] = layer_id; + + results[1] = 1; + + if (caps & DLCAPS_ALPHACHANNEL) { + /* Optimal! */ + scidfb_internal_caps |= SCIDFB_MAY_ALPHA; + return DFENUM_CANCEL; /* We're done */ + } + + /* Found something, but it might get better */ + return DFENUM_OK; +} + +#define LOOKING_FOR_POINTING_DEVICE 1 +#define LOOKING_FOR_KEYBOARD 2 +#define FOUND_IT 255 + +static DFBEnumerationResult +scidfb_input_callback(unsigned int device_id, DFBInputDeviceDescription descr, void *p) +{ + int *state = (int *) p; + int *result = state + 1; + + /* Check for mouse */ + if (*state == LOOKING_FOR_POINTING_DEVICE + && (descr.type & DIDTF_MOUSE)) { + *result = device_id; + *state = FOUND_IT; + return DFENUM_CANCEL; + } + + /* Check for keyboard */ + if (*state == LOOKING_FOR_KEYBOARD + && (descr.type & DIDTF_KEYBOARD)) { + *result = device_id; + *state = FOUND_IT; + return DFENUM_CANCEL; + } + + return DFENUM_OK; +} + +static int +scidfb_find_layer() +{ + unsigned int results[2]; + + results[1] = 0; + SCIDFB_CHECKED(scidfb_framebuffer->EnumDisplayLayers(scidfb_framebuffer, + scidfb_layer_callback, &results)); + + if (!results[1]) + return GFX_FATAL; /* No decent layer! */ + else { + SCIDFB_CHECKED(scidfb_framebuffer->GetDisplayLayer(scidfb_framebuffer, + results[0], + &scidfb_layer)); + + return GFX_OK; + } +} + +static int +_scidfb_init_input(int *found_keyboard, int *found_mouse) +{ + int inputs[2]; + + inputs[0] = LOOKING_FOR_POINTING_DEVICE; + SCIDFB_CHECKED(scidfb_framebuffer->EnumInputDevices(scidfb_framebuffer, + scidfb_input_callback, &inputs)); + + if ((*found_mouse = (inputs[0] == FOUND_IT))) { + IDirectFBInputDevice *mouse; + SCIDFB_CHECKED(scidfb_framebuffer->GetInputDevice(scidfb_framebuffer, + inputs[1], &mouse)); + + SCIDFB_CHECKED(mouse->CreateEventBuffer(mouse, &scidfb_input_buffer)); + } + + inputs[0] = LOOKING_FOR_KEYBOARD; + SCIDFB_CHECKED(scidfb_framebuffer->EnumInputDevices(scidfb_framebuffer, + scidfb_input_callback, &inputs)); + if ((*found_keyboard = (inputs[0] == FOUND_IT))) { + IDirectFBInputDevice *keyboard; + SCIDFB_CHECKED(scidfb_framebuffer->GetInputDevice(scidfb_framebuffer, + inputs[1], &keyboard)); + + if (scidfb_input_buffer) + SCIDFB_CHECKED(keyboard->AttachEventBuffer(keyboard, scidfb_input_buffer)) + else + SCIDFB_CHECKED(keyboard->CreateEventBuffer(keyboard, + &scidfb_input_buffer)); + } + + + SCIDFB_CHECKED(scidfb_framebuffer->CreateEventBuffer(scidfb_framebuffer, + DICAPS_KEYS + | DICAPS_AXES + | DICAPS_BUTTONS, + &scidfb_input_buffer)); +} + +static int +_scidfb_init_gfx_mode(int width, int height, int bpp) +{ + scidfb_mode_t modes[2]; + modes[0].x = width; + modes[0].y = height; + modes[0].bpp = bpp; + + modes[1].bpp = 0; /* nothing found yet */ + + SCIDFB_CHECKED(DirectFBCreate(&scidfb_framebuffer)); + SCIDFB_CHECKED(scidfb_framebuffer->EnumVideoModes(scidfb_framebuffer, + scidfb_mode_callback, &modes)); + + if (modes[1].bpp) { + DFBDisplayLayerConfig conf; + + if (scidfb_find_layer()) { + GFXERROR("DFB-GFX: No useable display layer found"); + return GFX_FATAL; + } + + SCIDFB_CHECKED(scidfb_framebuffer->SetVideoMode(scidfb_framebuffer, + modes[1].x, modes[1].y, modes[1].bpp)); + + SCIDFB_CHECKED(scidfb_layer->SetCooperativeLevel(scidfb_layer, DLSCL_EXCLUSIVE)); + SCIDFB_CHECKED(scidfb_layer->GetSurface(scidfb_layer, &scidfb_visual)); + + return GFX_OK; + } else + GFXERROR("DFB-GFX: No matching visual could be found!\n"); + + return GFX_FATAL; +} + + +static int +scidfb_build_virtual_surface(IDirectFBSurface **result, int width, int height, + IDirectFBSurface *prototype) +{ + /* This function creates a _virtual_ surface, i.e. a memory buffer, + ** without flipping support */ + DFBSurfaceDescription srf_desc; + IDirectFBSurface *surface; + + SCIDFB_CHECKED(prototype->GetCapabilities(prototype, &(srf_desc.caps))); + srf_desc.caps &= ~DSCAPS_FLIPPING; + srf_desc.width = width; + srf_desc.height = height; + SCIDFB_CHECKED(prototype->GetPixelFormat(prototype, &(srf_desc.pixelformat))); + srf_desc.flags = (DSDESC_CAPS | DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT); + SCIDFB_CHECKED(scidfb_framebuffer->CreateSurface(scidfb_framebuffer, &srf_desc, &surface)); + SCIDFB_CHECKED(surface->Clear(surface, 0, 0, 0, 255)); + + *result = surface; + + return GFX_OK; +} + +static int +scidfb_deactivate_pointer() +{ + SCIDFB_CHECKED(scidfb_layer->EnableCursor(scidfb_layer, 0)); + return 0; +} + + + /*----------------------------------------------------------------*/ +/*----------- Event queue implementation --------------*/ + /*----------------------------------------------------------------*/ + +#define MILLION 1000000 + +static int +scidfb_xlate_key(DFBInputDeviceKeyIdentifier key, int keysym) +{ + if ((keysym >= 'A') && (keysym <= 'Z')) + return keysym; + if ((keysym >= 'a') && (keysym <= 'z')) + return keysym; + if ((keysym >= '0') && (keysym <= '9')) + return keysym; + + switch (key) { + + case DIKI_0: return '0'; + case DIKI_1: return '1'; + case DIKI_2: return '2'; + case DIKI_3: return '3'; + case DIKI_4: return '4'; + case DIKI_5: return '5'; + case DIKI_6: return '6'; + case DIKI_7: return '7'; + case DIKI_8: return '8'; + case DIKI_9: return '9'; + + case DIKI_F1: return SCI_K_F1; + case DIKI_F2: return SCI_K_F2; + case DIKI_F3: return SCI_K_F3; + case DIKI_F4: return SCI_K_F4; + case DIKI_F5: return SCI_K_F5; + case DIKI_F6: return SCI_K_F6; + case DIKI_F7: return SCI_K_F7; + case DIKI_F8: return SCI_K_F8; + case DIKI_F9: return SCI_K_F9; + case DIKI_F10: return SCI_K_F10; + + case DIKI_KP_0: + case DIKI_INSERT: scidfb_buckybits ^= SCI_EVM_INSERT; + return SCI_K_INSERT; + + case DIKI_ESCAPE: return SCI_K_ESC; + case DIKI_LEFT: return SCI_K_LEFT; + case DIKI_RIGHT: return SCI_K_RIGHT; + case DIKI_UP: return SCI_K_UP; + case DIKI_DOWN: return SCI_K_DOWN; + case DIKI_TAB: return SCI_K_TAB; + case DIKI_ENTER: return SCI_K_ENTER; + case DIKI_SPACE: return ' '; + case DIKI_BACKSPACE: return SCI_K_BACKSPACE; + case DIKI_DELETE: return SCI_K_DELETE; + case DIKI_HOME: return SCI_K_HOME; + case DIKI_END: return SCI_K_END; + case DIKI_PAGE_UP: return SCI_K_PGDOWN; + case DIKI_PAGE_DOWN: return SCI_K_PGUP; + + case DIKI_QUOTE_LEFT: return '`'; + case DIKI_MINUS_SIGN: return '-'; + case DIKI_EQUALS_SIGN: return '='; + case DIKI_BRACKET_LEFT: return '['; + case DIKI_BRACKET_RIGHT: return ']'; + case DIKI_BACKSLASH: return '\\'; + case DIKI_SEMICOLON: return ';'; + case DIKI_QUOTE_RIGHT: return '\''; + case DIKI_COMMA: return ','; + case DIKI_SLASH: return '/'; + case DIKI_PERIOD: return '.'; + + case DIKI_KP_DIV: return '/'; + case DIKI_KP_MULT: return '*'; + case DIKI_KP_MINUS: return '-'; + case DIKI_KP_PLUS: return '+'; + case DIKI_KP_ENTER: return SCI_K_ENTER; + case DIKI_KP_SPACE: return ' '; + case DIKI_KP_TAB: return SCI_K_TAB; + case DIKI_KP_F1: return SCI_K_F1; + case DIKI_KP_F2: return SCI_K_F2; + case DIKI_KP_F3: return SCI_K_F3; + case DIKI_KP_F4: return SCI_K_F4; + case DIKI_KP_EQUAL: return '='; + + case DIKI_KP_DECIMAL: return SCI_K_DELETE; + case DIKI_KP_1: return SCI_K_END; + case DIKI_KP_2: return SCI_K_DOWN; + case DIKI_KP_3: return SCI_K_PGDOWN; + case DIKI_KP_4: return SCI_K_LEFT; + case DIKI_KP_5: return SCI_K_CENTER; + case DIKI_KP_6: return SCI_K_RIGHT; + case DIKI_KP_7: return SCI_K_HOME; + case DIKI_KP_8: return SCI_K_UP; + case DIKI_KP_9: return SCI_K_PGUP; + + default: + + } + return 0; /* Could not map key */ +} + + +scidfb_handle_bucky(int add, DFBInputDeviceKeyIdentifier key) +{ + int modifier = 0; + + switch(key) { + case DIKI_SHIFT_L: modifier = SCI_EVM_LSHIFT; + break; + case DIKI_SHIFT_R: modifier = SCI_EVM_RSHIFT; + break; + case DIKI_CONTROL_L: + case DIKI_CONTROL_R: modifier = SCI_EVM_CTRL; + break; + case DIKI_ALT_L: + case DIKI_ALT_R: + case DIKI_META_L: + case DIKI_META_R: modifier = SCI_EVM_ALT; + break; + case DIKI_CAPS_LOCK: modifier = SCI_EVM_CAPSLOCK; + break; + case DIKI_NUM_LOCK: modifier = SCI_EVM_NUMLOCK; + break; + case DIKI_SCROLL_LOCK: modifier = SCI_EVM_SCRLOCK; + break; + } + + if (add) + scidfb_buckybits |= modifier; + else + scidfb_buckybits &= ~modifier; +} + +static sci_event_t +scidfb_xlate_event(gfx_driver_t *drv, DFBEvent dfb_pre_evt) +{ + sci_event_t retval; + + retval.type = SCI_EVT_NONE; + retval.buckybits = scidfb_buckybits; + + if (dfb_pre_evt.clazz == DFEC_INPUT) { /* What kind of idiot named these field members? */ + DFBInputEvent dfbevt = dfb_pre_evt.input; + switch (dfbevt.type) { + + case DIET_KEYPRESS: + retval.type = SCI_EVT_KEYBOARD; + scidfb_handle_bucky(1, dfbevt.key_id); + retval.data = scidfb_xlate_key(dfbevt.key_id, dfbevt.key_symbol); + if (retval.data == 0) + retval.type = SCI_EVT_NONE; + break; + + case DIET_KEYRELEASE: + scidfb_handle_bucky(0, dfbevt.key_id); + break; + + case DIET_BUTTONPRESS: + retval.type = SCI_EVT_MOUSE_PRESS; + retval.data = dfbevt.button - DIBI_FIRST; + break; + + case DIET_AXISMOTION:{ + int *victim = NULL; + + if (dfbevt.axis == DIAI_X) + victim = &(drv->pointer_x); + else if (dfbevt.axis == DIAI_Y) + victim = &(drv->pointer_y); + else break; + + if (dfbevt.flags & DIEF_AXISABS) + *victim = dfbevt.axisabs; + else if (dfbevt.flags & DIEF_AXISREL) + *victim += dfbevt.axisrel; + } + break; + + case DIET_BUTTONRELEASE: + retval.type = SCI_EVT_MOUSE_RELEASE; + retval.data = dfbevt.button - DIBI_FIRST; + break; + } + } + + return retval; +} + +static void +scidfb_queue_event(sci_event_t evt) +{ + event_queue_t *node = malloc(sizeof(event_queue_t)); + event_queue_t **seekerp = &scidfb_event_queue; + + node->evt = evt; + node->next = NULL; + + while (*seekerp) + seekerp = &((*seekerp)->next); + + *seekerp = node; +} + + +static int +scidfb_queue_next_event(gfx_driver_t *drv, long micros_to_wait) +{ + DFBEvent dfb_event; + long secs = micros_to_wait / MILLION; + long usecs = (micros_to_wait - (secs * MILLION)) / 1000; + int has_event; + DFBResult timeout; + + timeout = scidfb_input_buffer->WaitForEventWithTimeout(scidfb_input_buffer, + secs, usecs); + if (timeout != DFB_TIMEOUT) + SCIDFB_CHECKED(timeout); + + has_event = scidfb_input_buffer->HasEvent(scidfb_input_buffer); + if (has_event == DFB_OK) { + sci_event_t evt; + /* We have a new event */ + SCIDFB_CHECKED(scidfb_input_buffer->GetEvent(scidfb_input_buffer, &dfb_event)); + evt = scidfb_xlate_event(drv, dfb_event); + + if (evt.type != SCI_EVT_NONE) + scidfb_queue_event(evt); + + return 0; + } else + if (has_event != DFB_BUFFEREMPTY) + return has_event; /* An error occured */ +} + + +static int +_scidfb_read_event(sci_event_t *evtp) +{ + if (scidfb_event_queue) { + event_queue_t *q = scidfb_event_queue; + *evtp = q->evt; + scidfb_event_queue = q->next; + free(q); + + return 1; + } else { + evtp->type = SCI_EVT_NONE; + return 0; + } +} + + + + + /*----------------------------------------------------------------*/ +/*----------- Implementations required by gfx_driver_t --------------*/ + /*----------------------------------------------------------------*/ + + +static int +scidfb_set_parameter(gfx_driver_t *drv, char *name, char *value) +{ + fprintf(stderr, "FIXME: These must be caught and moved in between DirectFBInit() and ..Create()\n"); + exit(1); + SCIDFB_CHECKED(DirectFBSetOption(name, value)); + return GFX_OK; +} + +static int +_scidfb_decode_pixel_format(DFBSurfacePixelFormat pixel_format, + int *rm, int *gm, int *bm, int *am, + int *rs, int *gs, int *bs, int *as, int *bytespp) +{ + /* Initially, the masks are set to the number of bits they occupy, and shifts + ** are left shifts */ + int _rs, _gs, _bs, _as; + + _as = 0; + *am = 0; + *as = 0; + + switch (pixel_format) { + + case DSPF_RGB15: + *bytespp = 2; + *rm = 5; _rs = 10; + *gm = 5; _gs = 5; + *bm = 5; _bs = 0; + break; + + case DSPF_RGB16: + *bytespp = 2; + *rm = 5; _rs = 11; + *gm = 6; _gs = 5; + *bm = 5; _bs = 0; + break; + + case DSPF_RGB24: + *bytespp = 3; + *rm = 8; _rs = 16; + *gm = 8; _gs = 8; + *bm = 8; _bs = 0; + break; + + case DSPF_RGB32: + *bytespp = 4; + *rm = 8; _rs = 16; + *gm = 8; _gs = 8; + *bm = 8; _bs = 0; + break; + + case DSPF_ARGB: + *bytespp = 4; + *am = 8; _as = 24; + *rm = 8; _rs = 16; + *gm = 8; _gs = 8; + *bm = 8; _bs = 0; + break; + + case DSPF_RGB332: + *bytespp = 1; + *rm = 3; _rs = 5; + *gm = 3; _gs = 2; + *bm = 2; _bs = 0; + break; + + default: + GFXERROR("DFB-GFX: Unknown/unsupported pixel format %08x\n", pixel_format); + return GFX_FATAL; + } + + /* Calculate correct shifts */ + *rs = 32 - *rm - _rs; + *gs = 32 - *gm - _gs; + *bs = 32 - *bm - _bs; + *as = 32 - *am - _as; + + /* Calculate correct masks */ + *rm = ((1 << (*rm)) - 1) << _rs; + *gm = ((1 << (*gm)) - 1) << _gs; + *bm = ((1 << (*bm)) - 1) << _bs; + *am = ((1 << (*am)) - 1) << _as; + + return GFX_OK; +} + +static int +scidfb_init_specific(gfx_driver_t *drv, int xres, int yres, int bytespp) +{ + DFBSurfacePixelFormat pixel_format; + char **p; + char *foo = ""; + int n = 1; + + int width, height; + int red_mask, green_mask, blue_mask, alpha_mask; + int red_shift, green_shift, blue_shift, alpha_shift; + int bytespp_real; + int mouse, keyboard; + + p = &foo; + DirectFBInit(&n, &p); + + SCIDFB_CHECKED(DirectFBSetOption("no-sighandler", "1")); + + SCIGFX_CHECKED(_scidfb_init_gfx_mode(xres, yres, (1 << bytespp) - 1)); + SCIGFX_CHECKED(_scidfb_init_input(&keyboard, &mouse)); + + if (!keyboard) { + GFXERROR("DFB-GFX: No keyboard found-- aborting...\n"); + return GFX_FATAL; + } + if (!mouse) { + GFXWARN("DFB-GFX: No pointing device found, disabling mouse support...\n"); + drv->capabilities &= ~GFX_CAPABILITY_MOUSE_SUPPORT; + } + + SCIDFB_CHECKED(scidfb_visual->GetSize(scidfb_visual, &width, &height)); + + scidfb_deactivate_pointer(); + SCIDFB_CHECKED(scidfb_visual->Clear(scidfb_visual, 0, 0, 0, 255)); + SCIDFB_CHECKED(scidfb_visual->Flip(scidfb_visual, NULL, DSFLIP_BLIT)); + + + /* If neccessary, create a sub-surface */ + if (width > xres * 320 || height > yres * 200) { + IDirectFBSurface *subsurface; + DFBRectangle region; + region.w = xres * 320; + region.h = yres * 200; + region.x = (width - region.w) >> 1; + region.y = (height - region.h) >> 1; + + SCIDFB_CHECKED(scidfb_visual->GetSubSurface(scidfb_visual, ®ion, &subsurface)); + + scidfb_visual = subsurface; + } + + scidfb_visual->SetDrawingFlags(scidfb_visual, DSDRAW_BLEND); /* Unchecked: It's just a + ** feature */ + SCIDFB_CHECKED(scidfb_visual->GetPixelFormat(scidfb_visual, &pixel_format)); + + SCIGFX_CHECKED(_scidfb_decode_pixel_format(pixel_format, + &red_mask, &green_mask, &blue_mask, &alpha_mask, + &red_shift, &green_shift, &blue_shift, &alpha_shift, + &bytespp_real)); + + fprintf(stderr, "Mode %08x -> (%x>>%d, %x>>%d, %x>>%d, %x>>%d) at %d bytespp\n", + pixel_format, + red_mask, red_shift, + green_mask, green_shift, + blue_mask, blue_shift, + alpha_mask, alpha_shift, + bytespp_real); + + drv->mode = gfx_new_mode(xres, yres, + bytespp_real, + red_mask, green_mask, blue_mask, alpha_mask, + red_shift, green_shift, blue_shift, alpha_shift, + 0 /* not palette mode*/, + 0); + + SCIGFX_CHECKED(scidfb_build_virtual_surface(&scidfb_static_visual, + drv->mode->xfact * 320, + drv->mode->yfact * 200, + scidfb_visual)); + + scidfb_back_priority = + gfx_pixmap_alloc_index_data(gfx_new_pixmap(drv->mode->xfact * 320, + drv->mode->yfact * 200, + GFX_RESID_NONE, 0, 0)); + + return GFX_OK; +} + +static int +_scidfb_set_color(gfx_color_t *c) +{ + SCIDFB_CHECKED(scidfb_visual->SetColor(scidfb_visual, + c->visual.r, c->visual.g, c->visual.b, + (c->mask & GFX_MASK_VISUAL)? (255 - c->alpha) : 0)); + return GFX_OK; +} + +static int +scidfb_init(gfx_driver_t *drv) +{ + return scidfb_init_specific(drv, 2, 2, 1); +} + + +static void +scidfb_exit(gfx_driver_t *drv) +{ + gfx_free_pixmap(drv, scidfb_back_priority); +} + +static int +scidfb_draw_line(gfx_driver_t *drv, point_t start, point_t end, gfx_color_t color, + gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + int xc, yc; + + SCIGFX_CHECKED(_scidfb_set_color(&color)); + + if (line_mode == GFX_LINE_MODE_FINE) { + SCIDFB_CHECKED(scidfb_visual->DrawLine(scidfb_visual, + start.x, start.y, + end.x, end.y)); + } else /* "Correct" lines */ + for (xc = 0; xc < drv->mode->xfact; xc++) + for (yc = 0; yc < drv->mode->yfact; yc++) + SCIDFB_CHECKED(scidfb_visual->DrawLine(scidfb_visual, + start.x + xc, + start.y + yc, + end.x + xc, + end.y + yc)); + + return GFX_OK; +} + +static int +scidfb_draw_filled_rect(gfx_driver_t *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + SCIGFX_CHECKED(_scidfb_set_color(&color1)); + SCIDFB_CHECKED(scidfb_visual->FillRectangle(scidfb_visual, + rect.x, rect.y, + rect.xl, rect.yl)); + + return GFX_OK; +} + +static int +scidfb_register_pixmap(gfx_driver_t *drv, gfx_pixmap_t *pxm) +{ + return GFX_OK; +} + + +static int +scidfb_unregister_pixmap(gfx_driver_t *drv, gfx_pixmap_t *pxm) +{ + return GFX_OK; +} + + +static int +scidfb_draw_pixmap(gfx_driver_t *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + void *_dest_data; + byte *dest_data; + int line_width; + IDirectFBSurface *visual = (buffer == GFX_BUFFER_STATIC)? + scidfb_static_visual : scidfb_visual; + gfx_pixmap_t *priority_map = (buffer == GFX_BUFFER_STATIC)? + scidfb_static_priority : scidfb_back_priority; + + SCIDFB_CHECKED(visual->Lock(visual, DSLF_WRITE | DSLF_READ, + &_dest_data, &line_width)); + dest_data = (byte *) _dest_data; + + gfx_crossblit_pixmap(drv->mode, pxm, priority, src, dest, dest_data, + line_width, + priority_map->index_data, priority_map->index_xl, + 1, 0); + + SCIDFB_CHECKED(visual->Unlock(visual)); +} + +static int +scidfb_grab_pixmap(gfx_driver_t *drv, rect_t src, gfx_pixmap_t *pxm, + gfx_map_mask_t map) +{ + void *_src_data; + byte *src_data; + byte *dest = pxm->data; + int line_width; + int write_width = drv->mode->bytespp * (src.xl); + + pxm->xl = src.xl; + pxm->yl = src.yl; + + if (map != GFX_MASK_VISUAL) { + fprintf(stderr, "Not trying to read from visual mask-- not supported!"); + return GFX_FATAL; + } + + SCIDFB_CHECKED(scidfb_visual->Lock(scidfb_visual, DSLF_READ | DSLF_WRITE, + /* Must use DSLF_WRITE to choose the back buffer, + ** otherwise the front buffer would be chosen */ + &_src_data, &line_width)); + src_data = (byte *) _src_data; + + src_data += (drv->mode->bytespp * src.x) + + (line_width * src.y); /* left upper corner of the source */ + + if (src.y < 0){ + fprintf(stderr, "src.y=%d\n", src.y); + exit(1); + } + + while (src.yl--) { + memcpy(dest, src_data, write_width); + src_data += line_width; + dest += write_width; + } + + + SCIDFB_CHECKED(scidfb_visual->Unlock(scidfb_visual)); +} + +static int +scidfb_update(gfx_driver_t *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + if (src.x != dest.x + || src.y != dest.y) { + GFXERROR("DFB-GFX: Attempt to update from (%d,%d,%d,%d) to (%d,%d)\n", + GFX_PRINT_RECT(src), dest.x, dest.y); + } + + if (buffer == GFX_BUFFER_FRONT) { + DFBRegion region; + region.x1 = src.x; + region.y1 = src.y; + region.x2 = src.x + src.xl; + region.y2 = src.y + src.yl; + + SCIDFB_CHECKED(scidfb_visual->Flip(scidfb_visual, ®ion, DSFLIP_BLIT)); + } else { /* Back buffer update */ + DFBRectangle dest_rect; + dest_rect.x = src.x; + dest_rect.y = src.y; + dest_rect.w = src.xl; + dest_rect.h = src.yl; + + SCIDFB_CHECKED(scidfb_visual->Blit(scidfb_visual, + scidfb_static_visual, + &dest_rect, + dest.x, dest.y)); + + /* Now update the priority map: */ + if (scidfb_static_priority) /* only if the buffers have been initialized */ + gfx_copy_pixmap_box_i(scidfb_back_priority, scidfb_static_priority, src); + } + + return GFX_OK; +} + +static int +scidfb_set_static_buffer(gfx_driver_t *drv, gfx_pixmap_t *pic, gfx_pixmap_t *priority) +{ + void *_dest; + byte *dest; + byte *src = pic->data; + int line_width; + int draw_width = pic->xl * drv->mode->bytespp; + int i; + + scidfb_static_priority = priority; + SCIDFB_CHECKED(scidfb_static_visual->Lock(scidfb_static_visual, DSLF_WRITE, + &_dest, &line_width)); + dest = (byte *) _dest; + + for (i = 0; i < pic->yl; i++) { + memcpy(dest, src, draw_width); + dest += line_width; + src += draw_width; + } + + SCIDFB_CHECKED(scidfb_static_visual->Unlock(scidfb_static_visual)); + + return GFX_OK; +} + +static void +_normalize_pointer(gfx_driver_t *drv) +{ + int maxw = drv->mode->xfact * 320; + int maxh = drv->mode->yfact * 200; + + if (drv->pointer_x < 0) + drv->pointer_x = 0; + if (drv->pointer_x >= maxw) + drv->pointer_x = maxw - 1; + if (drv->pointer_y < 0) + drv->pointer_y = 0; + if (drv->pointer_y >= maxh) + drv->pointer_y = maxh - 1; +} + + +static sci_event_t +scidfb_get_event(gfx_driver_t *drv) +{ + sci_event_t retval; + + scidfb_queue_next_event(drv, 0); + _scidfb_read_event(&retval); + + _normalize_pointer(drv); + + + return retval; +} + +static int +scidfb_usec_sleep(gfx_driver_t *drv, long usecs) +{ + scidfb_queue_next_event(drv, usecs); + _normalize_pointer(drv); + return GFX_OK; +} + +gfx_driver_t gfx_driver_dfb = { + "dfb", + SCIDFB_DRIVER_VERSION, + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, + 0, 0, + GFX_CAPABILITY_MOUSE_SUPPORT + | GFX_CAPABILITY_FINE_LINES, + 0, + scidfb_set_parameter, + scidfb_init_specific, + scidfb_init, + scidfb_exit, + scidfb_draw_line, + scidfb_draw_filled_rect, + scidfb_register_pixmap, + scidfb_unregister_pixmap, + scidfb_draw_pixmap, + scidfb_grab_pixmap, + scidfb_update, + scidfb_set_static_buffer, + NULL, + NULL, + scidfb_get_event, + scidfb_usec_sleep, + NULL +}; + +#endif diff --git a/engines/sci/gfx/drivers/dx_driver.cpp b/engines/sci/gfx/drivers/dx_driver.cpp new file mode 100644 index 0000000000..899d1fa70c --- /dev/null +++ b/engines/sci/gfx/drivers/dx_driver.cpp @@ -0,0 +1,1179 @@ +/*************************************************************************** + dx_driver.cpp Copyright (C) 2008 Alexander R Angas, + Some portions Copyright (C) 1999 Dmitry Jemerov + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Alexander R Angas (Alex) + + History: + 20051106 (AAngas) - Rewrite + 20060201 (AAngas) - Fixed wrong format for texture priority maps + 20060205 (AAngas) - Changed pointer to use D3DXSprite + 20060208 (AAngas) - Fixed pointer alpha blending bug + 20060307 (AAngas) - Fixed crash on exit + 20080118 (AAngas) - Fixed pointer scaling and window size + + Notes: + DirectX handles all scaling. All drawing functions assume an unscaled + resolution. + +TODO: + P1 - Corrupt cursor at different resolutions + P1 - Lost devices + P1 - Problems moving and activating window + P1 - Occasional pause when starting FreeSCI and poor sound resulting (?) + P1 - Mouse pointer corruption at bottom and why need to scale vertical by 4? + P2 - Full screen + P3 - Draw lines as Direct3D vertices + P3 - Fine line mode + P3 - Allow user to specify hardware or software vertex processing + P3 - Fancies + +***************************************************************************/ + +#ifdef HAVE_DIRECTX + +#ifndef __cplusplus +#error NOTE: This file MUST be compiled as C++. In Visual C++, use the /Tp command line option. +#endif + +#include "dx_driver.h" + +#define TO_STRING2(x) #x +#define TO_STRING(x) TO_STRING2(x) +#if (DIRECT3D_VERSION < 0x0800) +# error The DirectX 8 SDK (or higher) is required for this driver. +#elif (DIRECT3D_VERSION > 0x0800) +# pragma message (" ") +# pragma message ("*** This driver has been developed against version 8 of the DirectX SDK and may not work against your version " TO_STRING(DIRECT3D_VERSION)) +# pragma message (" ") +#endif + + +// Stores driver flags +static int flags = 0; + + +// Windows message processing +LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + return DefWindowProc( hWnd, msg, wParam, lParam ); +} + + +///// RENDERING + +// Render the scene to screen +static gfx_return_value_t +RenderD3D(struct _gfx_driver *drv) +{ + HRESULT hr; + + // Check we haven't lost the device (i.e. window focus) + if (CheckDevice(drv)) + { + // Begin scene + DODX( (dx_state->pDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0, 0)), RenderD3D ); + DODX( (dx_state->pDevice->BeginScene()), RenderD3D ); + + // Set texture + DODX( (dx_state->pDevice->SetTexture( 0, dx_state->pTexVisuals[PRIMARY_VIS] )), RenderD3D ); // Scene image + + // Set texture states for scene + DODX( (dx_state->pDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE )), RenderD3D ); + DODX( (dx_state->pDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE )), RenderD3D ); + DODX( (dx_state->pDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE )), RenderD3D ); + + // Set vertices and how to draw + DODX( (dx_state->pDevice->SetStreamSource(0, dx_state->pVertBuff, sizeof(CUSTOMVERTEX))), RenderD3D ); + DODX( (dx_state->pDevice->SetVertexShader( D3DFVF_CUSTOMVERTEX )), RenderD3D ); + DODX( (dx_state->pDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2)), RenderD3D ); + + // Draw the pointer sprite + if (dx_state->pTexPointer) + { + D3DXVECTOR2 pointerPos((float)drv->pointer_x, (float)drv->pointer_y); + DODX( (dx_state->pSPointer->Begin()), RenderD3D ); + DODX( (dx_state->pSPointer->Draw(dx_state->pTexPointer, NULL, NULL, NULL, 0.0, &pointerPos, 0xFFFFFFFF)), RenderD3D ); + DODX( (dx_state->pSPointer->End()), RenderD3D ); + } + + // Present scene + DODX( (dx_state->pDevice->EndScene()), RenderD3D ); + DODX( (dx_state->pDevice->Present(NULL, NULL, NULL, NULL)), RenderD3D ); + } + + return GFX_OK; +} + + +// Check device hasn't been lost +static int +CheckDevice(struct _gfx_driver *drv) +{ + HRESULT hr; + switch ( dx_state->pDevice->TestCooperativeLevel() ) + { + case D3DERR_DEVICELOST: return false; // Lost window focus + + case D3DERR_DEVICENOTRESET: // We're back! + { + // Reset with our presentation parameters and reinitialise the scene + DODX( (dx_state->pDevice->Reset(&dx_state->presParams)), CheckDevice ); + if (hr != D3D_OK) + return false; + + InitScene(drv); + return true; + } + + default: return true; + } +} + + +///// INITIALIZATION + +// Create window to draw to +static gfx_return_value_t +InitWindow(struct _gfx_driver *drv, UINT width, UINT height) +{ + DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + DWORD dwStyle = WS_OVERLAPPEDWINDOW; + RECT clientSize = { 0, 0, width, height }; + sciprintf("Window %d x %d\n", width, height); + + // Register the window class + ZeroMemory( &(dx_state->wc), sizeof(WNDCLASSEX) ); + dx_state->wc.cbSize = sizeof(WNDCLASSEX); + dx_state->wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + dx_state->wc.lpfnWndProc = MsgProc; + dx_state->wc.hInstance = GetModuleHandle(NULL); + dx_state->wc.lpszClassName = DX_CLASS_NAME; + if ( RegisterClassEx( &dx_state->wc ) == 0 ) + { + sciprintf("InitWindow(): RegisterClassEx failed (%u)\n", GetLastError()); + return GFX_FATAL; + } + + // Get correct size of window to pass to CreateWindow (considering window decorations) + if ( AdjustWindowRectEx( &clientSize, dwStyle, false, dwExStyle ) == 0 ) + { + sciprintf("InitWindow(): Window size calculation failed (%u)\n", GetLastError()); + return GFX_FATAL; + } + + // Create the application's window + dx_state->hWnd = CreateWindowEx( + dwExStyle, + DX_CLASS_NAME, DX_APP_NAME, + dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + CW_USEDEFAULT, CW_USEDEFAULT, + clientSize.right - clientSize.left, clientSize.bottom - clientSize.top, + NULL/*GetDesktopWindow()*/, NULL, dx_state->wc.hInstance, NULL ); + + if ( dx_state->hWnd == NULL ) + { + sciprintf("InitWindow(): CreateWindow failed (%u)\n", GetLastError()); + return GFX_FATAL; + } + + // Show the window + ShowWindow( dx_state->hWnd, SW_SHOWDEFAULT ); + UpdateWindow( dx_state->hWnd ); + + return GFX_OK; +} + + +// Initialize Direct3D +static gfx_return_value_t +InitD3D(struct _gfx_driver *drv) +{ + HRESULT hr; + + sciprintf("Initializing Direct3D\n"); + + // Set our colour format + dx_state->d3dFormat = D3DFMT_A8R8G8B8; + dx_state->vertexProcessing = D3DCREATE_MIXED_VERTEXPROCESSING; + + // Create Direct3D object + dx_state->pD3d = Direct3DCreate8( D3D_SDK_VERSION ); + if ( FAILED( dx_state->pD3d ) ) { + sciprintf("InitD3D(): Direct3DCreate8 failed\n"); + return GFX_FATAL; + } + + // Look for adapters + for ( UINT adapterLoop = 0; adapterLoop < dx_state->pD3d->GetAdapterCount(); adapterLoop++ ) + { + D3DADAPTER_IDENTIFIER8 adapterId; + DODX( (dx_state->pD3d->GetAdapterIdentifier(adapterLoop, D3DENUM_NO_WHQL_LEVEL, &adapterId)), InitD3D ); + if ( FAILED( hr ) ) + break; + if (adapterId.Driver[0] == '\0') + break; + sciprintf(" Adapter %u: %s\n", adapterLoop++, adapterId.Description); + } + if (dx_state->adapterId == -1) + dx_state->adapterId = D3DADAPTER_DEFAULT; + + // Get device caps + DODX( (dx_state->pD3d->GetDeviceCaps(dx_state->adapterId, D3DDEVTYPE_HAL, &(dx_state->deviceCaps))), InitD3D ); + if ( FAILED( hr ) ) { + sciprintf("Sorry, this adapter does not have a 3D accelerated video driver installed.\n"); + return GFX_FATAL; + } + + // Define presentation parameters + ZeroMemory( &dx_state->presParams, sizeof(D3DPRESENT_PARAMETERS) ); + dx_state->presParams.Windowed = TRUE; // We want windowed by default + dx_state->presParams.SwapEffect = D3DSWAPEFFECT_DISCARD; // Throw away last frame + dx_state->presParams.hDeviceWindow = dx_state->hWnd; // Window handle + dx_state->presParams.BackBufferWidth = 320; // Back buffer dimensions + dx_state->presParams.BackBufferHeight = 200; // + dx_state->presParams.BackBufferFormat = dx_state->d3dFormat; // Colour format + + // Check if user requested full screen + if (flags & DX_FLAGS_FULLSCREEN) + { + if (dx_state->deviceCaps.Caps2 & D3DCAPS2_CANRENDERWINDOWED) { + dx_state->presParams.Windowed = FALSE; + sciprintf("Full screen mode"); + } else { + sciprintf("Sorry, DirectX will not render in full screen with your video card\n"); + } + } + + // Get current display mode + DODX( (dx_state->pD3d->GetAdapterDisplayMode( dx_state->adapterId, &dx_state->displayMode )), InitD3D ); + sciprintf("Chosen adapter %u\n", dx_state->adapterId); + + // Turn off Windows mouse pointer + ShowCursor(FALSE); + + return GFX_OK; +} + + +// Initialize scene +static gfx_return_value_t +InitScene(struct _gfx_driver *drv) +{ + HRESULT hr; + + sciprintf("Creating scene\n"); + + // Describe how scene will be rendered + DODX((dx_state->pDevice->SetRenderState( D3DRS_AMBIENT, RGB(255,255,255) )), InitScene); // Maximum ambient light + DODX((dx_state->pDevice->SetRenderState( D3DRS_LIGHTING, FALSE )), InitScene); // Disable lighting features + DODX((dx_state->pDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE )), InitScene); // Don't cull back side of polygons + DODX((dx_state->pDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_FALSE )), InitScene); // No depth buffering + + return GFX_OK; +} + + +// For user to set a driver-specific parameter +static int +dx_set_param(struct _gfx_driver *drv, char *attribute, char *value) +{ +/* // Full screen + if (!strncmp(attribute, "fullscreen", 11)) { + if (string_truep(value)) + flags |= DX_FLAGS_FULLSCREEN; + else + flags &= ~DX_FLAGS_FULLSCREEN; + + return GFX_OK; + } +*/ + // Adapter ID + if (!strncmp(attribute, "adapterid", 11)) { + int aid = D3DADAPTER_DEFAULT; + dx_state->adapterId = atoi(value); + + return GFX_OK; + } + + sciprintf("Unrecognised attempt to set DirectX parameter \"%s\" to \"%s\"\n", attribute, value); + return GFX_ERROR; +} + + +// Initialize a specific graphics mode +static int +dx_init_specific(struct _gfx_driver *drv, + int xfact, int yfact, /* horizontal and vertical scaling */ + int bytespp) /* must be value 4 */ +{ + HRESULT hr; + int red_shift = 8, green_shift = 16, blue_shift = 24, alpha_shift = 32; + int alpha_mask = 0x00000000, red_mask = 0x00ff0000, green_mask = 0x0000ff00, blue_mask = 0x000000ff; + gfx_return_value_t d3dret; + + // Error checking + if (xfact < 1 || yfact < 1 || bytespp < 1 || bytespp > 4) { + sciprintf("Attempt to open window with invalid scale factors (%d,%d) and bpp=%d!\n", + xfact, yfact, bytespp); + return GFX_ERROR; + } + + // Prepare memory for gfx_dx_struct_t + drv->state = (struct gfx_dx_struct_t *) sci_malloc(sizeof(gfx_dx_struct_t)); + ZeroMemory(drv->state, sizeof(gfx_dx_struct_t)); + dx_state->adapterId = -1; // we will set this later + + // Set window size factor + dx_state->xfact = xfact; + dx_state->yfact = yfact; + dx_state->bpp = bytespp; + sciprintf("Window scaling %d x %d @ %d bpp\n", dx_state->xfact, dx_state->yfact, dx_state->bpp << 3); + + // Set up Direct3D + d3dret = InitD3D(drv); + if (d3dret != GFX_OK) + return d3dret; + + // Create window + InitWindow(drv, dx_state->xfact * 320, dx_state->yfact * 200); + + // Create D3D device + DODX( (dx_state->pD3d->CreateDevice(dx_state->adapterId, D3DDEVTYPE_HAL, dx_state->hWnd, + dx_state->vertexProcessing, &dx_state->presParams, &dx_state->pDevice)), dx_init_specific ); + + // Create the scene + d3dret = InitScene(drv); + if (d3dret != GFX_OK) + return d3dret; + + // Define and populate vertex buffers + DODX((dx_state->pDevice->CreateVertexBuffer( 4 * sizeof(CUSTOMVERTEX), D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, + D3DPOOL_MANAGED, &dx_state->pVertBuff )), dx_init_specific); + + dx_state->pvData[0].p = D3DXVECTOR4( 0.0f, 0.0f, 0.0f, 1.0f); + dx_state->pvData[1].p = D3DXVECTOR4(320.0f, 0.0f, 0.0f, 1.0f); + dx_state->pvData[2].p = D3DXVECTOR4( 0.0f, 200.0f, 0.0f, 1.0f); + dx_state->pvData[3].p = D3DXVECTOR4(320.0f, 200.0f, 0.0f, 1.0f); + dx_state->pvData[0].colour = dx_state->pvData[1].colour = dx_state->pvData[2].colour = dx_state->pvData[3].colour = 0xffffffff; + dx_state->pvData[0].t = D3DXVECTOR2(0.0f, 0.0f); + dx_state->pvData[1].t = D3DXVECTOR2(1.0f, 0.0f); + dx_state->pvData[2].t = D3DXVECTOR2(0.0f, 1.0f); + dx_state->pvData[3].t = D3DXVECTOR2(1.0f, 1.0f); + + VOID *ptr; + DODX((dx_state->pVertBuff->Lock(0, 0, (BYTE**)&ptr, 0)), dx_init_specific); + memcpy(ptr, dx_state->pvData, sizeof(dx_state->pvData)); + DODX((dx_state->pVertBuff->Unlock()), dx_init_specific); + + // Create textures + int i; + for (i = 0; i < NUM_VISUAL_BUFFERS; i++) + { + DODX((dx_state->pDevice->CreateTexture(320, 200, + 1, 0, + dx_state->d3dFormat, + D3DPOOL_MANAGED, + &dx_state->pTexVisuals[i])), dx_init_specific); + } + for (i = 0; i < NUM_PRIORITY_BUFFERS; i++) + { + DODX((dx_state->pDevice->CreateTexture(320, 200, + 1, 0, + dx_state->d3dFormat, + D3DPOOL_MANAGED, + &dx_state->pTexPrioritys[i])), dx_init_specific); + } + + // Create sprite for pointer + DODX( (D3DXCreateSprite(dx_state->pDevice, &dx_state->pSPointer)), dx_init_specific ); + + // Allocate priority maps + for (int i = 0; i < NUM_PRIORITY_BUFFERS; i++) + { + dx_state->priority_maps[i] = gfx_pixmap_alloc_index_data(gfx_new_pixmap(320, 200, GFX_RESID_NONE, -i, -777)); + if (!dx_state->priority_maps[i]) { + GFXERROR("Out of memory: Could not allocate priority maps! (%dx%d)\n", 320, 200); + return GFX_FATAL; + } + dx_state->priority_maps[i]->flags |= GFX_PIXMAP_FLAG_SCALED_INDEX; + } + + // Set up the event queue + dx_state->queue_first = 0; + dx_state->queue_last = 0; + dx_state->queue_size = 256; + dx_state->event_queue = (sci_event_t *) sci_malloc (dx_state->queue_size * sizeof (sci_event_t)); + + // Set up graphics mode for primary vis + drv->mode = gfx_new_mode(1, 1, dx_state->bpp, + red_mask, green_mask, blue_mask, alpha_mask, + red_shift, green_shift, blue_shift, alpha_shift, + (dx_state->bpp == 1) ? 256 : 0, 0); + + // Set up graphics mode for pointer + dx_state->pointerMode = gfx_new_mode(1, 4, dx_state->bpp, + 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000, + 24, 16, 8, 0, + 0, GFX_MODE_FLAG_REVERSE_ALPHA); + + return GFX_OK; +} + + +// Initialize 'most natural' graphics mode +static int +dx_init(struct _gfx_driver *drv) +{ + return dx_init_specific(drv, 2, 2, 4); +} + + +// Uninitialize the current graphics mode +static void +dx_exit(struct _gfx_driver *drv) +{ + int i; + + if(drv->state == NULL) + return; + + for (i = 0; i < NUM_PRIORITY_BUFFERS; i++) + SAFE_RELEASE( dx_state->pTexPrioritys[i] ); + for (i = 0; i < NUM_VISUAL_BUFFERS; i++) + SAFE_RELEASE( dx_state->pTexVisuals[i] ); + SAFE_RELEASE( dx_state->pTexPointer ); + SAFE_RELEASE( dx_state->pSPointer ); + SAFE_RELEASE( dx_state->pVertBuff ); + SAFE_RELEASE( dx_state->pDevice ); + SAFE_RELEASE( dx_state->pD3d ); + + if ( dx_state->pointerMode ) + gfx_free_mode(dx_state->pointerMode); + if ( drv->mode ) + gfx_free_mode(drv->mode); + + if ( dx_state->event_queue ) + sci_free(dx_state->event_queue); + dx_state->queue_size = 0; + + for (i = 0; i < NUM_PRIORITY_BUFFERS; i++) + gfx_free_pixmap(drv, dx_state->priority_maps[i]); + + UnregisterClass( DX_CLASS_NAME, dx_state->wc.hInstance ); + DestroyWindow(dx_state->hWnd); + + sci_free(dx_state); +} + + +///// DRAWING + +// Draws a single filled and possibly shaded rectangle to the back buffer +static int +dx_draw_filled_rect(struct _gfx_driver *drv, rect_t box, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + if (color1.mask & GFX_MASK_VISUAL) + { + HRESULT hr; + D3DLOCKED_RECT lockedRect; + + // Calculate colour value for line pixel + UINT lineColor = (color1.alpha << 24) | (color1.visual.r << 16) | (color1.visual.g << 8) | color1.visual.b; + RECT r = { box.x, box.y, box.x + box.xl, box.y + box.yl }; + RECT lr = r; + + // Fix bounds + if (lr.left == lr.right) + lr.right++; + if (lr.top == lr.bottom) + lr.bottom++; + if ((UINT)r.right > 320) + lr.right = r.right = 320; + if ((UINT)r.bottom > 200) + lr.bottom = r.bottom = 200; + + sciprintf("dx_draw_filled_rect(): %08X %i,%i -> %i,%i\n", lineColor, r.left, r.top, r.right, r.bottom); + + DODX( (dx_state->pTexVisuals[BACK_VIS]->LockRect(0, &lockedRect, &lr, 0)), dx_draw_filled_rect ); + UINT *rectPtr = (UINT*)lockedRect.pBits; + + // Going along x axis + for (int y_pixel = r.top; y_pixel < r.bottom; y_pixel++) + { + UINT *startLine = rectPtr; + for (int x_pixel = r.left; x_pixel < r.right; x_pixel++) + { + *rectPtr = lineColor; + rectPtr++; + } + rectPtr = startLine; + rectPtr += 320; + } + + DODX( (dx_state->pTexVisuals[BACK_VIS]->UnlockRect(0)), dx_draw_filled_rect ); + } + + if (color1.mask & GFX_MASK_PRIORITY) + { + byte *pri; + pri = dx_state->priority_maps[BACK_PRI]->index_data + box.x + box.y*(320); + for(int i=0; i %i,%i\n", line_rect.x, line_rect.y, line_rect.xl + line_rect.x, line_rect.yl + line_rect.y); + + // Make sure priorities are not updated in dx_draw_filled_rect() + gfx_color_t col = color; + col.mask = GFX_MASK_VISUAL; + + dx_draw_filled_rect(drv, line_rect, col, col, GFX_SHADE_FLAT); + } + + if (color.mask & GFX_MASK_PRIORITY) { + gfx_draw_line_pixmap_i(dx_state->priority_maps[BACK_PRI], start, end, color.priority); + } + + return GFX_OK; +} + + +///// PIXMAPS + +// Register the pixmap as a texture +static int +dx_register_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + HRESULT hr; + + int i, xs; + byte *s, *d; + D3DLOCKED_RECT lockedRect; + LPDIRECT3DTEXTURE8 newTex; + DODX( (dx_state->pDevice->CreateTexture(pxm->xl, pxm->yl, 1, 0, dx_state->d3dFormat, D3DPOOL_MANAGED, &newTex )), dx_register_pixmap ); + + // Do gfx crossblit + DODX( (newTex->LockRect(0, &lockedRect, NULL, 0)), dx_register_pixmap ); + s = pxm->data; + d = (byte *) lockedRect.pBits; + xs = drv->mode->bytespp * pxm->xl; + + for(i = 0; i < pxm->yl; i++) + { + memcpy(d, s, xs); + s += xs; + d += lockedRect.Pitch; + } + DODX( (newTex->UnlockRect(0)), dx_register_pixmap ); + + pxm->internal.info = newTex; + pxm->internal.handle = SCI_DX_HANDLE_NORMAL; + + return GFX_OK; +} + + +// Unregister the pixmap +static int +dx_unregister_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + SAFE_RELEASE((LPDIRECT3DTEXTURE8) (pxm->internal.info)); + pxm->internal.info = NULL; + + return GFX_OK; +} + + +// Draws part of a pixmap to the static or back buffer +static int +dx_draw_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + HRESULT hr; + int bufnr = (buffer == GFX_BUFFER_STATIC) ? 2 : 1; + int pribufnr = bufnr - 1; + LPDIRECT3DTEXTURE8 srct, dstt; + LPDIRECT3DSURFACE8 sbuf, dbuf; + D3DLOCKED_RECT lockedRect; + byte *pri_map = NULL; + + if (pxm->internal.handle == SCI_DX_HANDLE_GRABBED) { + // copy from pxm->internal.info to visual[bufnr] + RECT srcRect = RECT_T_TO_RECT(src); + POINT dstPoint = { dest.x, dest.y }; + + srct = (LPDIRECT3DTEXTURE8) (pxm->internal.info); + dstt = dx_state->pTexVisuals[bufnr]; + + DODX( (srct->GetSurfaceLevel(0, &sbuf)), dx_draw_pixmap ); + DODX( (dstt->GetSurfaceLevel(0, &dbuf)), dx_draw_pixmap ); + DODX( (dx_state->pDevice->CopyRects(sbuf, &srcRect, 1, dbuf, &dstPoint)), dx_draw_pixmap ); + + SAFE_RELEASE(sbuf); + SAFE_RELEASE(dbuf); + + return GFX_OK; + } + + + // Create texture to temporarily hold visuals[bufnr] + LPDIRECT3DTEXTURE8 temp; + DODX( (dx_state->pDevice->CreateTexture(pxm->xl, pxm->yl, 1, 0, dx_state->d3dFormat, D3DPOOL_MANAGED, &temp)), dx_draw_pixmap ); + RECT srcRect = RECT_T_TO_RECT(dest); + RECT destRect = { 0, 0, dest.xl, dest.yl }; + POINT dstPoint = { destRect.left, destRect.top }; + + // Copy from visuals[bufnr] to temp + srct = dx_state->pTexVisuals[bufnr]; + dstt = temp; + DODX( (srct->GetSurfaceLevel(0, &sbuf)), dx_draw_pixmap ); + DODX( (dstt->GetSurfaceLevel(0, &dbuf)), dx_draw_pixmap ); + DODX( (dx_state->pDevice->CopyRects(sbuf, &srcRect, 1, dbuf, &dstPoint)), dx_draw_pixmap ); + + // Copy from given pixmap to temp + DODX( (dbuf->LockRect(&lockedRect, &destRect, 0)), dx_draw_pixmap ); + gfx_crossblit_pixmap(drv->mode, pxm, priority, src, dest, + (byte *) lockedRect.pBits, lockedRect.Pitch, + dx_state->priority_maps[pribufnr]->index_data, + dx_state->priority_maps[pribufnr]->index_xl, 1, + GFX_CROSSBLIT_FLAG_DATA_IS_HOMED); + DODX( (dbuf->UnlockRect()), dx_draw_pixmap ); + + SAFE_RELEASE(sbuf); + SAFE_RELEASE(dbuf); + + + // Copy from temp to visuals[bufnr] + RECT src2Rect = { 0, 0, dest.xl, dest.yl }; + POINT dst2Point = { dest.x, dest.y }; + + srct = temp; + dstt = dx_state->pTexVisuals[bufnr]; + DODX( (srct->GetSurfaceLevel(0, &sbuf)), dx_draw_pixmap ); + DODX( (dstt->GetSurfaceLevel(0, &dbuf)), dx_draw_pixmap ); + DODX( (dx_state->pDevice->CopyRects(sbuf, &src2Rect, 1, dbuf, &dst2Point)), dx_draw_pixmap ); + + SAFE_RELEASE(sbuf); + SAFE_RELEASE(dbuf); + SAFE_RELEASE(temp); + + return GFX_OK; +} + + +// Grabs an image from the visual or priority back buffer +static int +dx_grab_pixmap(struct _gfx_driver *drv, rect_t src, gfx_pixmap_t *pxm, + gfx_map_mask_t map) +{ + HRESULT hr; + + if (src.x < 0 || src.y < 0) { + GFXERROR("Attempt to grab pixmap from invalid coordinates (%d,%d)\n", src.x, src.y); + return GFX_ERROR; + } + + if (!pxm->data) { + GFXERROR("Attempt to grab pixmap to unallocated memory\n"); + return GFX_ERROR; + } + + // Choose map to grab from + switch (map) { + + case GFX_MASK_VISUAL: { + LPDIRECT3DTEXTURE8 temp; + LPDIRECT3DSURFACE8 tempSrf, backSrf; + CONST RECT srcRect = RECT_T_TO_RECT(src); + CONST POINT dstPoint = { 0, 0 }; + + pxm->xl = src.xl; + pxm->yl = src.yl; + + DODX( (dx_state->pDevice->CreateTexture(pxm->xl, pxm->yl, 1, 0, dx_state->d3dFormat, D3DPOOL_MANAGED, &temp)), dx_grab_pixmap ); + + DODX( (dx_state->pTexVisuals[BACK_VIS]->GetSurfaceLevel(0, &backSrf)), dx_grab_pixmap ); + DODX( (temp->GetSurfaceLevel(0, &tempSrf)), dx_grab_pixmap ); + DODX( (dx_state->pDevice->CopyRects(backSrf, &srcRect, 1, tempSrf, &dstPoint)), dx_grab_pixmap ); + + pxm->internal.info = temp; + pxm->internal.handle = SCI_DX_HANDLE_GRABBED; + pxm->flags |= GFX_PIXMAP_FLAG_INSTALLED | GFX_PIXMAP_FLAG_EXTERNAL_PALETTE | GFX_PIXMAP_FLAG_PALETTE_SET; + + SAFE_RELEASE(backSrf); + SAFE_RELEASE(tempSrf); + + break; + } + + case GFX_MASK_PRIORITY: + sciprintf("FIXME: priority map grab not implemented yet!\n"); + break; + + default: + sciprintf("Attempt to grab pixmap from invalid map 0x%02x\n", map); + return GFX_ERROR; + } + + return GFX_OK; +} + + +///// BUFFERING + +// Updates the front buffer or the back buffers +static int +dx_update(struct _gfx_driver *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + HRESULT hr; + LPDIRECT3DTEXTURE8 srct, dstt; + LPDIRECT3DSURFACE8 sbuf, dbuf; + CONST RECT srcRect = RECT_T_TO_RECT(src); + CONST POINT dstPoint = { dest.x, dest.y }; + + switch (buffer) { + + case GFX_BUFFER_FRONT: + srct = dx_state->pTexVisuals[BACK_VIS]; + dstt = dx_state->pTexVisuals[PRIMARY_VIS]; + + DODX( (srct->GetSurfaceLevel(0, &sbuf)), dx_update ); + DODX( (dstt->GetSurfaceLevel(0, &dbuf)), dx_update ); + + DODX( (dx_state->pDevice->CopyRects(sbuf, &srcRect, 1, dbuf, &dstPoint)), dx_update ); + + SAFE_RELEASE(sbuf); + SAFE_RELEASE(dbuf); + + RenderD3D(drv); + break; + + case GFX_BUFFER_BACK: + if (src.x == dest.x && src.y == dest.y) + gfx_copy_pixmap_box_i(dx_state->priority_maps[BACK_PRI], dx_state->priority_maps[STATIC_PRI], src); + + srct = dx_state->pTexVisuals[STATIC_VIS]; + dstt = dx_state->pTexVisuals[BACK_VIS]; + + DODX( (srct->GetSurfaceLevel(0, &sbuf)), dx_update ); + DODX( (dstt->GetSurfaceLevel(0, &dbuf)), dx_update ); + + DODX( (dx_state->pDevice->CopyRects(sbuf, &srcRect, 1, dbuf, &dstPoint)), dx_update ); + + SAFE_RELEASE(sbuf); + SAFE_RELEASE(dbuf); + + break; + + default: + GFXERROR("Invalid buffer %d in update!\n", buffer); + return GFX_ERROR; + } + + return GFX_OK; +} + + +// Sets the contents of the static visual and priority buffers +static int +dx_set_static_buffer(struct _gfx_driver *drv, gfx_pixmap_t *pic, + gfx_pixmap_t *priority) +{ + if (!pic->internal.info) { + GFXERROR("Attempt to set static buffer with unregistered pixmap!\n"); + return GFX_ERROR; + } + + HRESULT hr; + LPDIRECT3DTEXTURE8 pii = (LPDIRECT3DTEXTURE8) (pic->internal.info); + LPDIRECT3DSURFACE8 pbf; + LPDIRECT3DTEXTURE8 vis = dx_state->pTexVisuals[STATIC_VIS]; + LPDIRECT3DSURFACE8 vbf; + + // Copy from pic to visual[static] + DODX( (pii->GetSurfaceLevel(0, &pbf)), dx_set_static_buffer ); + DODX( (vis->GetSurfaceLevel(0, &vbf)), dx_set_static_buffer ); + DODX( (dx_state->pDevice->CopyRects(pbf, NULL, 0, vbf, NULL)), dx_set_static_buffer ); + + SAFE_RELEASE(pbf); + SAFE_RELEASE(vbf); + + // Copy priority map + gfx_copy_pixmap_box_i(dx_state->priority_maps[STATIC_PRI], priority, gfx_rect(0, 0, 320, 200)); + + return GFX_OK; +} + + +///// MOUSE + +// Sets a new mouse pointer +static int +dx_set_pointer(struct _gfx_driver *drv, gfx_pixmap_t *pointer) +{ + HRESULT hr; + + if (pointer->data == NULL) + return GFX_OK; + pointer->yl /= 2; // Scale the pointer to compensate for mode scale + + LPDIRECT3DTEXTURE8 pntTex; + LPDIRECT3DSURFACE8 pntSrf; + + // Get pointer dimensions and init + POINTS pDims = {pointer->xl, pointer->yl}; + dx_state->pointerDims = pDims; + RECT r = {0, 0, pointer->xl, pointer->yl}; + + // Recreate pointer data according to the graphics mode we need + gfx_pixmap_free_data(pointer); + gfx_xlate_pixmap(pointer, dx_state->pointerMode, GFX_XLATE_FILTER_NONE); + + // Create texture and fill with pointer data + DODX( (dx_state->pDevice->CreateTexture(pointer->xl, pointer->yl, 1, 0, dx_state->d3dFormat, D3DPOOL_MANAGED, &pntTex )), dx_set_pointer ); + DODX( (pntTex->GetSurfaceLevel(0, &pntSrf)), dx_set_pointer ); + + DODX( (D3DXLoadSurfaceFromMemory(pntSrf, NULL, &r, pointer->data, dx_state->d3dFormat, 256, NULL, &r, D3DX_FILTER_NONE, 0)), dx_set_pointer); + + SAFE_RELEASE(pntSrf); + + // Assign as current pointer texture + if (dx_state->pTexPointer) + SAFE_RELEASE(dx_state->pTexPointer); + dx_state->pTexPointer = pntTex; + + return GFX_OK; +} + + +// Display mouse pointer +static int +show_pointer(struct _gfx_driver *drv, LPARAM pos) +{ + POINTS mousePos; // mouse coordinates + + // Sort out coordinates + mousePos = MAKEPOINTS(pos); + + // Update pos + drv->pointer_x = mousePos.x; + drv->pointer_y = mousePos.y; + + RenderD3D(drv); + + return GFX_OK; +} + + +///// EVENTS + +// Get event from the queue +static sci_event_t +get_queue_event(gfx_dx_struct_t *ctx) +{ + if (ctx->queue_first == ctx->queue_size) + ctx->queue_first = 0; + + if (ctx->queue_first == ctx->queue_last) + { + sci_event_t noevent; + noevent.data = 0; + noevent.type = SCI_EVT_NONE; + noevent.buckybits = 0; + return noevent; + } + else + return ctx->event_queue [ctx->queue_first++]; +} + + +// Add event to the queue +static void add_queue_event(gfx_dx_struct_t *ctx, int type, int data, short buckybits) +{ + if ((ctx->queue_last+1) % ctx->queue_size == ctx->queue_first) + { + /* Reallocate queue */ + int i, event_count; + sci_event_t *new_queue; + + new_queue = (sci_event_t *) sci_malloc (ctx->queue_size * 2 * sizeof (sci_event_t)); + event_count = (ctx->queue_last - ctx->queue_first) % ctx->queue_size; + for (i=0; ievent_queue [(ctx->queue_first+i) % ctx->queue_size]; + free (ctx->event_queue); + ctx->event_queue = new_queue; + ctx->queue_size *= 2; + ctx->queue_first = 0; + ctx->queue_last = event_count; + } + + ctx->event_queue [ctx->queue_last].data = data; + ctx->event_queue [ctx->queue_last].type = type; + ctx->event_queue [ctx->queue_last++].buckybits = buckybits; + if (ctx->queue_last == ctx->queue_size) + ctx->queue_last=0; +} + + +// Add keystroke event to queue +static void add_key_event (gfx_dx_struct_t *ctx, int data) +{ + short buckybits = 0; + + if (GetAsyncKeyState (VK_RSHIFT)) + buckybits |= SCI_EVM_RSHIFT; + if (GetAsyncKeyState (VK_LSHIFT)) + buckybits |= SCI_EVM_LSHIFT; + if (GetAsyncKeyState (VK_CONTROL)) + buckybits |= SCI_EVM_CTRL; + if (GetAsyncKeyState (VK_MENU)) + buckybits |= SCI_EVM_ALT; + if (GetKeyState (VK_SCROLL) & 1) + buckybits |= SCI_EVM_SCRLOCK; + if (GetKeyState (VK_NUMLOCK) & 1) + buckybits |= SCI_EVM_NUMLOCK; + if (GetKeyState (VK_CAPITAL) & 1) + buckybits |= SCI_EVM_CAPSLOCK; + + add_queue_event (ctx, SCI_EVT_KEYBOARD, data, buckybits); +} + + +// Add mouse event to queue +static void add_mouse_event(gfx_dx_struct_t *ctx, int type, int data, WPARAM wParam) +{ + short buckybits = 0; + if (wParam & MK_SHIFT) + buckybits |= SCI_EVM_LSHIFT | SCI_EVM_RSHIFT; + if (wParam & MK_CONTROL) + buckybits |= SCI_EVM_CTRL; + + add_queue_event (ctx, type, data, buckybits); +} + + +// Returns the next event in the event queue for this driver +static sci_event_t +dx_get_event(struct _gfx_driver *drv) +{ + assert(drv->state != NULL); + return get_queue_event(dx_state); +} + + +// Sleeps the specified amount of microseconds, or until the mouse moves +static int +dx_usleep(struct _gfx_driver *drv, long usecs) +{ + if (usecs < 1000) + { + sleep(0); + } + else + { + sleep(usecs/1000); + } + ProcessMessages(drv); + + return GFX_OK; +} + + +// Process any Windows messages +static int +ProcessMessages(struct _gfx_driver *drv) +{ + MSG msg; + + while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) { + switch( msg.message ) + { + case WM_PAINT: + ValidateRect( dx_state->hWnd, NULL ); + RenderD3D(drv); + break; + + case WM_KEYDOWN: + switch (msg.wParam) + { + MAP_KEY (VK_ESCAPE, SCI_K_ESC); + MAP_KEY (VK_BACK, SCI_K_BACKSPACE); + MAP_KEY (VK_RETURN, SCI_K_ENTER); + MAP_KEY (VK_TAB, SCI_K_TAB); + MAP_KEY (VK_END, SCI_K_END); + MAP_KEY (VK_DOWN, SCI_K_DOWN); + MAP_KEY (VK_NEXT, SCI_K_PGDOWN); + MAP_KEY (VK_LEFT, SCI_K_LEFT); + MAP_KEY (VK_RIGHT, SCI_K_RIGHT); + MAP_KEY (VK_HOME, SCI_K_HOME); + MAP_KEY (VK_UP, SCI_K_UP); + MAP_KEY (VK_PRIOR, SCI_K_PGUP); + MAP_KEY (VK_INSERT, SCI_K_INSERT); + MAP_KEY (VK_DELETE, SCI_K_DELETE); + MAP_KEY (VK_DECIMAL, SCI_K_DELETE); + /* TODO: Glutton no longer had SCI_K etc... declared + MAP_KEY (VK_ADD, SCI_K_PLUS); + MAP_KEY (VK_OEM_PLUS, SCI_K_EQUALS); + MAP_KEY (VK_SUBTRACT, SCI_K_MINUS); + MAP_KEY (VK_OEM_MINUS, SCI_K_MINUS); + MAP_KEY (VK_MULTIPLY, SCI_K_MULTIPLY); + MAP_KEY (VK_DIVIDE, SCI_K_DIVIDE); + */ + MAP_KEY (VK_OEM_COMMA, ','); + MAP_KEY (VK_OEM_PERIOD, '.'); + MAP_KEY (VK_OEM_1, ';'); // US keyboards only + MAP_KEY (VK_OEM_2, '/'); + MAP_KEY (VK_OEM_3, '`'); + MAP_KEY (VK_OEM_4, '['); + MAP_KEY (VK_OEM_5, '\\'); + MAP_KEY (VK_OEM_6, ']'); + MAP_KEY (VK_OEM_7, '\''); + MAP_KEY (VK_F1, SCI_K_F1); + MAP_KEY (VK_F2, SCI_K_F2); + MAP_KEY (VK_F3, SCI_K_F3); + MAP_KEY (VK_F4, SCI_K_F4); + MAP_KEY (VK_F5, SCI_K_F5); + MAP_KEY (VK_F6, SCI_K_F6); + MAP_KEY (VK_F7, SCI_K_F7); + MAP_KEY (VK_F8, SCI_K_F8); + MAP_KEY (VK_F9, SCI_K_F9); + MAP_KEY (VK_F10, SCI_K_F10); + + case VK_RSHIFT: + case VK_LSHIFT: + case VK_CONTROL: + case VK_MENU: + case VK_SCROLL: + case VK_NUMLOCK: + case VK_CAPITAL: + break; // ignore + + default: + if (msg.wParam >= 'A' && msg.wParam <= 'Z') + add_key_event (dx_state, msg.wParam - 'A' + 97); + else if (msg.wParam >= VK_NUMPAD0 && msg.wParam <= VK_NUMPAD9) + { + if (GetKeyState (VK_NUMLOCK) & 1) + add_key_event (dx_state, msg.wParam - VK_NUMPAD0 + '0'); + else + switch (msg.wParam) + { + MAP_KEY (VK_NUMPAD0, SCI_K_INSERT); + MAP_KEY (VK_NUMPAD1, SCI_K_END); + MAP_KEY (VK_NUMPAD2, SCI_K_DOWN); + MAP_KEY (VK_NUMPAD3, SCI_K_PGDOWN); + MAP_KEY (VK_NUMPAD4, SCI_K_LEFT); + MAP_KEY (VK_NUMPAD6, SCI_K_RIGHT); + MAP_KEY (VK_NUMPAD7, SCI_K_HOME); + MAP_KEY (VK_NUMPAD8, SCI_K_UP); + MAP_KEY (VK_NUMPAD9, SCI_K_PGUP); + } + } + else if (msg.wParam == 0xC0) // tilde key - used for invoking console + add_key_event (dx_state, '`'); + else + add_key_event (dx_state, msg.wParam); + break; + } + break; + + case WM_MOUSEMOVE: + // Turn off mouse cursor + ShowCursor(FALSE); + show_pointer(drv, msg.lParam); + break; + + case WM_MOUSELEAVE: + // Turn on mouse cursor + ShowCursor(TRUE); + break; + + case WM_LBUTTONDOWN: add_mouse_event (dx_state, SCI_EVT_MOUSE_PRESS, 1, msg.wParam); break; + case WM_RBUTTONDOWN: add_mouse_event (dx_state, SCI_EVT_MOUSE_PRESS, 2, msg.wParam); break; + case WM_MBUTTONDOWN: add_mouse_event (dx_state, SCI_EVT_MOUSE_PRESS, 3, msg.wParam); break; + case WM_LBUTTONUP: add_mouse_event (dx_state, SCI_EVT_MOUSE_RELEASE, 1, msg.wParam); break; + case WM_RBUTTONUP: add_mouse_event (dx_state, SCI_EVT_MOUSE_RELEASE, 2, msg.wParam); break; + case WM_MBUTTONUP: add_mouse_event (dx_state, SCI_EVT_MOUSE_RELEASE, 3, msg.wParam); break; + + case WM_DESTROY: + PostQuitMessage( 0 ); + drv->exit(drv); + exit(-1); + break; + + } + } + + return 0; +} + + +extern "C" +gfx_driver_t gfx_driver_dx = { + "directx", + "0.4.2", + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, /* mode */ + 0, 0, /* mouse pointer position */ + GFX_CAPABILITY_MOUSE_SUPPORT | GFX_CAPABILITY_MOUSE_POINTER | GFX_CAPABILITY_COLOR_MOUSE_POINTER | GFX_CAPABILITY_PIXMAP_REGISTRY | GFX_CAPABILITY_WINDOWED, + 0, + dx_set_param, + dx_init_specific, + dx_init, + dx_exit, + dx_draw_line, + dx_draw_filled_rect, + dx_register_pixmap, + dx_unregister_pixmap, + dx_draw_pixmap, + dx_grab_pixmap, + dx_update, + dx_set_static_buffer, + dx_set_pointer, + NULL, + dx_get_event, + dx_usleep, + NULL +}; + +#endif diff --git a/engines/sci/gfx/drivers/dx_driver.h b/engines/sci/gfx/drivers/dx_driver.h new file mode 100644 index 0000000000..3eb280cefb --- /dev/null +++ b/engines/sci/gfx/drivers/dx_driver.h @@ -0,0 +1,151 @@ +/*************************************************************************** + graphics_directx.h Copyright (C) 2008 Alexander R Angas, + Some portions Copyright (C) 1999 Dmitry Jemerov + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Alexander R Angas (Alex) + +***************************************************************************/ + +#include +#include +#include +#include + +extern "C" { +#include +#include +#include +#include +#include +#include +#include // for sciprintf +#include +#include +}; + +// Error trapping, every DirectX call should use this +#define DODX(cmd, proc) \ + hr = cmd; \ + if (hr != S_OK) { \ + sciprintf("%s, %i, %i, %s from %s\n", __FILE__, __LINE__, hr, #cmd, #proc); \ + DXTrace(__FILE__, __LINE__, hr, #cmd" from "#proc, 1); \ + } + + +// Easily release only allocated objects +#define SAFE_RELEASE(p) \ + if (p) \ + (p)->Release(); + + +// Make it simple to access drv->state +#define dx_state ((struct gfx_dx_struct_t *)(drv->state)) + + +// Simply map a key using add_key_event() +#define MAP_KEY(x,y) case x: add_key_event ((struct gfx_dx_struct_t *)(drv->state), y); break + + +#define DX_CLASS_NAME "FreeSCI DirectX Graphics" +#define DX_APP_NAME "FreeSCI" + +// Vertex format +#define D3DFVF_CUSTOMVERTEX ( D3DFVF_DIFFUSE | D3DFVF_XYZRHW | D3DFVF_TEX1 ) + +// Vertex structure +struct CUSTOMVERTEX +{ + D3DXVECTOR4 p; // Vertex coordinates + DWORD colour; // Colour + D3DXVECTOR2 t; // Texture coordinates +}; + +#define SCI_DX_HANDLE_NORMAL 0 +#define SCI_DX_HANDLE_GRABBED 1 + +// Number of buffers for each type of texture +#define NUM_VISUAL_BUFFERS 3 +#define NUM_PRIORITY_BUFFERS 2 + +// What each buffer references +#define PRIMARY_VIS 0 +#define BACK_VIS 1 +#define STATIC_VIS 2 + +#define BACK_PRI 0 +#define STATIC_PRI 1 + +// Struct that holds everything +struct gfx_dx_struct_t +{ + D3DFORMAT d3dFormat; // Colour format + UINT adapterId; // Adapter ID to use + DWORD vertexProcessing; // Hardware or software vertex processing + + LPDIRECT3D8 pD3d; // D3D object + D3DCAPS8 deviceCaps; // Capabilities of device + D3DDISPLAYMODE displayMode; // Width and height of screen + D3DPRESENT_PARAMETERS presParams; // Presentation parameters + LPDIRECT3DDEVICE8 pDevice; // Rendering device + + LPDIRECT3DVERTEXBUFFER8 pVertBuff; // Buffer to hold pixmap vertices + CUSTOMVERTEX pvData[4]; // Buffer of pixmap vertex structs + + LPDIRECT3DTEXTURE8 pTexVisuals[NUM_VISUAL_BUFFERS]; // Array of visual textures + LPDIRECT3DTEXTURE8 pTexPrioritys[NUM_PRIORITY_BUFFERS]; // Array of priority textures + gfx_pixmap_t *priority_maps[NUM_PRIORITY_BUFFERS]; // Array of SCI priority maps + + gfx_mode_t *pointerMode; // SCI graphics mode for pointer + LPDIRECT3DTEXTURE8 pTexPointer; // Mouse pointer texture + LPD3DXSPRITE pSPointer; // Mouse pointer sprite + POINTS pointerDims; // Pointer dimensions + + WNDCLASSEX wc; // Window class + HWND hWnd; // Window + UINT xfact, yfact; // Scaling factors + UINT bpp; // Bits per pixel + + // Event queue + int queue_size, queue_first, queue_last; + sci_event_t *event_queue; +}; + +// Flags that may be set in the driver +#define DX_FLAGS_FULLSCREEN 1 + +// Initialization functions +static int +ProcessMessages(struct _gfx_driver *drv); + +static gfx_return_value_t +RenderD3D(struct _gfx_driver *drv); + +static int +CheckDevice(struct _gfx_driver *drv); + +static gfx_return_value_t +InitWindow(struct _gfx_driver *drv, UINT width, UINT height); + +static gfx_return_value_t +InitD3D(struct _gfx_driver *drv); + +static gfx_return_value_t +InitScene(struct _gfx_driver *drv); diff --git a/engines/sci/gfx/drivers/gfx_drivers.c b/engines/sci/gfx/drivers/gfx_drivers.c new file mode 100644 index 0000000000..b47d6467c9 --- /dev/null +++ b/engines/sci/gfx/drivers/gfx_drivers.c @@ -0,0 +1,192 @@ +/*************************************************************************** + gfx_drivers.c Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include + + +static char *oldname = NULL; +static void *oldhandle; + + + +#ifndef HAVE_DLOPEN +# ifdef HAVE_LIBGGI +extern gfx_driver_t gfx_driver_ggi; +# endif + + +# ifndef X_DISPLAY_MISSING +extern gfx_driver_t gfx_driver_xlib; +# endif + +# ifdef HAVE_DIRECTX +extern gfx_driver_t gfx_driver_dx; +# endif + +# ifdef HAVE_DDRAW +extern gfx_driver_t gfx_driver_dd; +# endif + +# ifdef HAVE_SDL +extern gfx_driver_t gfx_driver_sdl; +# endif + +# ifdef HAVE_DIRECTFB +extern gfx_driver_t gfx_driver_dfb; +# endif + +# ifdef _DREAMCAST +extern gfx_driver_t gfx_driver_dc; +# endif +#endif + +extern gfx_driver_t gfx_driver_null; + +static gfx_driver_t *gfx_drivers[] = { +#ifndef HAVE_DLOPEN +# ifndef X_DISPLAY_MISSING + &gfx_driver_xlib, +# endif +# ifdef HAVE_SDL + &gfx_driver_sdl, +# endif +# ifdef HAVE_DIRECTX + &gfx_driver_dx, +# endif +# ifdef HAVE_DDRAW + &gfx_driver_dd, +# endif +# ifdef HAVE_DIRECTFB + &gfx_driver_dfb, +# endif +# ifdef HAVE_LIBGGI + &gfx_driver_ggi, +# endif +# ifdef _DREAMCAST + &gfx_driver_dc, +# endif +#endif + &gfx_driver_null, + NULL +}; + +#define DRIVER_TYPE "gfx" +#define DRIVER_PREFIX "gfx_driver_" +#define DRIVER_FILE_SUFFIX "_driver" + +#ifdef HAVE_DLOPEN +struct _gfx_driver * +gfx_find_driver(char *path, char *name) +{ + int retval = 0; + + if (oldhandle) + sci_close_module(oldhandle, DRIVER_TYPE, oldname); + + if (!name) { /* Find default driver */ +#ifdef _WIN32 + name = "sdl"; +#else /* !_WIN32 */ +# ifndef X_DISPLAY_MISSING + if (getenv("DISPLAY")) + name = "xlib"; + else +# endif + name = "ggi"; +#endif /* !_WIN32 */ + } + + oldname = name; + return (struct _gfx_driver *) + sci_find_module(path, name, DRIVER_TYPE, + DRIVER_PREFIX, + DRIVER_FILE_SUFFIX, + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + &oldhandle); +} +#else /* No dlopen */ +struct _gfx_driver * +gfx_find_driver(char *path, char *name) +{ + int retval = 0; + + if (!name) { /* Find default driver */ +#ifndef X_DISPLAY_MISSING + if (getenv("DISPLAY")) + return &gfx_driver_xlib; +#endif +#if defined (MACOSX) && defined(HAVE_SDL) + return &gfx_driver_sdl; +#endif + return gfx_drivers[0]; + } + + while (gfx_drivers[retval] && strcasecmp(name, gfx_drivers[retval]->name)) + retval++; + + return gfx_drivers[retval]; +} +#endif + + +const char * +gfx_get_driver_name(int nr) +{ + return (gfx_drivers[nr])? gfx_drivers[nr]->name : NULL; +} + + +int +string_truep(char *value) +{ + return !(strcasecmp(value, "ok") && + strcasecmp(value, "enable") && + strcasecmp(value, "1") && + strcasecmp(value, "true") && + strcasecmp(value, "yes") && + strcasecmp(value, "on")); +} + + +int +string_falsep(char *value) +{ + return !(strcasecmp(value, "disable") && + strcasecmp(value, "0") && + strcasecmp(value, "false") && + strcasecmp(value, "no") && + strcasecmp(value, "off")); +} + + + + diff --git a/engines/sci/gfx/drivers/ggi_driver.c b/engines/sci/gfx/drivers/ggi_driver.c new file mode 100644 index 0000000000..c6cf4b0f00 --- /dev/null +++ b/engines/sci/gfx/drivers/ggi_driver.c @@ -0,0 +1,1035 @@ +/*************************************************************************** + ggi_driver.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* FreeSCI 0.3.1+ graphics driver module for libggi */ + + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBGGI + +#include +#include + + +#define GGI_DRIVER_VERSION "0.4" + +#define GFX_GGI_DEBUG + +#ifdef GFX_GGI_DEBUG + +#define POSMSG sciprintf("%s L%d:", __FILE__, __LINE__) +#define DEBUG_POINTER (!(drv->debug_flags & GFX_DEBUG_POINTER))? 0 : POSMSG && gfxprintf +#define DEBUG_UPDATES (!(drv->debug_flags & GFX_DEBUG_UPDATES))? 0 : POSMSG && gfxprintf +#define DEBUG_PIXMAPS (!(drv->debug_flags & GFX_DEBUG_PIXMAPS))? 0 : POSMSG && sciprintf +#define DEBUG_BASIC (!(drv->debug_flags & GFX_DEBUG_BASIC))? 0 : POSMSG && sciprintf +#else /* !GFX_GGI_DEBUG */ +#define DEBUG_POINTER (1)? 0 : +#define DEBUG_UPDATES (1)? 0 : +#define DEBUG_PIXMAPS (1)? 0 : +#define DEBUG_BASIC (1)? 0 : +#endif /* !GFX_GGI_DEBUG */ + +static gfx_mode_t * +_aberr(char *file, int line, char *message); + +static void +init_input_ggi(); + +#define MODE ((drv->mode)? drv->mode : _aberr(__FILE__, __LINE__, "drv->mode is NULL")) +#define STATE ((gfx_ggi_struct_t *)drv->state) +#define VISUAL ((gfx_ggi_struct_t *)drv->state)->vis +#define FRAMES ((gfx_ggi_struct_t *)drv->state)->frames + +static gfx_mode_t * +_aberr(char *file, int line, char *message) +{ + fprintf(stderr,"GFXGGI: Fatal: %s L%d: %s\n", file, line, message); + exit(1); /* Die */ + return NULL; +} + +#define GGI_BUFFER_BACK 0 +#define GGI_BUFFER_STATIC 1 + +#define SCI_GGI_SWAP_CTRL_CAPS (1 << 0) + +typedef struct { + ggi_visual_t vis; + int frames; + + byte *alt_back_buffer; /* if frames < 2: Virtual back buffer */ + ggi_visual_t back_vis; /* Memory visual for the back buffer */ + + byte *static_buffer; /* if frames < 3: Virtual static buffer */ + ggi_visual_t static_vis; /* Memory visual for the static buffer */ + + gfx_pixmap_t *priority_maps[2]; + ggi_visual_t priority_visuals[2]; /* Visuals for the maps */ + + int x_blank, y_blank; + int x_blank2, y_blank2; + +} gfx_ggi_struct_t; + +static int flags; + +static int +ggi_set_param(gfx_driver_t *drv, char *attribute, char *value) +{ + gfx_ggi_struct_t *meta = (gfx_ggi_struct_t *) drv->state; + + if (!strcmp(attribute, "swap_ctrl_caps") || + !strcmp(attribute, "swap_caps_ctrl")) { + if (string_truep(value)) + flags |= SCI_GGI_SWAP_CTRL_CAPS; + else + flags &= ~SCI_GGI_SWAP_CTRL_CAPS; + + return GFX_OK; + } + + DEBUG_BASIC("ggi_set_param('%s' to '%s')\n", attribute, value); + return GFX_ERROR; +} + + +static int +_open_meta_visuals(gfx_driver_t *drv) +{ + int i; + + for (i = 0; i < 2; i++) { + if (!(STATE->priority_visuals[i] = ggiOpen("memory:pointer", STATE->priority_maps[i]->data))) { + sciprintf("GFXGGI: Could not open priority map #%d\n", i); + return 1; + } + if (ggiSetSimpleMode(STATE->priority_visuals[i], 320 * MODE->xfact, 200 * MODE->yfact, 1, GT_8BIT)) { + sciprintf("GFXGGI: Could not set mode for priority visual %d\n", i); + return 1; + } + } + + return 0; +} + +static int +ggi_init_specific(gfx_driver_t *drv, int xres, int yres, int bpp) +{ + gfx_ggi_struct_t *meta; + ggi_graphtype graphtype; + const ggi_pixelformat *pixelformat; + int frames = 3; + + switch (bpp) { + case 1: graphtype = GT_8BIT; break; + case 2: graphtype = GT_16BIT; break; + case 3: graphtype = GT_24BIT; break; + case 4: graphtype = GT_32BIT; break; + default: sciprintf("GFXGGI: Error: Invalid bytes per pixel value: %d\n", bpp); + return GFX_ERROR; + } + + drv->state = NULL; + + if (ggiInit() < 0) + return GFX_FATAL; + + meta = (gfx_ggi_struct_t *) sci_calloc(sizeof(gfx_ggi_struct_t), 1); + + if (!(meta->vis = ggiOpen(NULL))) { + DEBUG_BASIC("ggiOpen() failed!\n"); + free(meta); + ggiExit(); + return GFX_FATAL; + } + + while ((frames > 0) && (ggiSetSimpleMode(meta->vis, xres * 320, yres * 200, frames, graphtype))) + --frames; + + if (!frames) { + DEBUG_BASIC("Initializing %dx%d at %d bpp failed\n", xres*320, yres*200, bpp << 3); + free(meta); + ggiExit(); + return GFX_ERROR; + } + + DEBUG_BASIC("Initialized with %d frames\n", frames); + + meta->frames = frames; + + pixelformat = ggiGetPixelFormat(meta->vis); + drv->mode = gfx_new_mode(xres, yres, pixelformat->size >> 3, + pixelformat->red_mask, pixelformat->green_mask, pixelformat->blue_mask, + 0, /* alpha mask */ + pixelformat->red_shift, pixelformat->green_shift, pixelformat->blue_shift, + 0, /* alpha shift */ + (bpp == 1)? 256 : 0, 0); + drv->state = meta; + + meta->priority_maps[GGI_BUFFER_BACK] = + gfx_pixmap_alloc_index_data(gfx_new_pixmap(320 * xres, 200 * yres, GFX_RESID_NONE, 0, 0)); + meta->priority_maps[GGI_BUFFER_STATIC] = + gfx_pixmap_alloc_index_data(gfx_new_pixmap(320 * xres, 200 * yres, GFX_RESID_NONE, 0, 0)); + + meta->priority_maps[GGI_BUFFER_BACK]->flags |= GFX_PIXMAP_FLAG_SCALED_INDEX; + meta->priority_maps[GGI_BUFFER_STATIC]->flags |= GFX_PIXMAP_FLAG_SCALED_INDEX; + + if (_open_meta_visuals(drv)) { + free(meta); + gfx_free_pixmap(drv, meta->priority_maps[GGI_BUFFER_BACK]); + gfx_free_pixmap(drv, meta->priority_maps[GGI_BUFFER_STATIC]); + ggiClose(meta->vis); + ggiExit(); + return GFX_ERROR; + } + + if (frames < 2) { + meta->alt_back_buffer = (byte *) sci_malloc(bpp * 320 * 200 * xres * yres); + meta->back_vis = ggiOpen("memory:pointer", meta->alt_back_buffer, NULL); + if (ggiSetSimpleMode(meta->back_vis, xres * 320, yres * 200, 1, GT_8BIT)) { + sciprintf("GFXGGI: Warning: Setting mode for memory visual failed\n"); + } + } else meta->alt_back_buffer = NULL; + + if (frames < 3) { + meta->static_buffer = (byte *) sci_malloc(bpp * 320 * 200 * xres * yres); + meta->static_vis = ggiOpen("memory:pointer", meta->static_buffer, NULL); + if (ggiSetSimpleMode(meta->static_vis, xres * 320, yres * 200, 1, GT_8BIT)) { + sciprintf("GFXGGI: Warning: Setting mode for memory visual #2 failed\n"); + } + } else meta->static_buffer = NULL; + + init_input_ggi(); + flags = 0; + + return GFX_OK; +} + + +static int +ggi_init(gfx_driver_t *drv) +{ + gfx_ggi_struct_t *meta; + ggi_mode mode; + int x_blank, y_blank; + const ggi_pixelformat *pixelformat; + mode.frames = 3; + mode.visible.x = mode.visible.y = mode.virt.x = mode.virt.y + = mode.size.x = mode.size.y = mode.dpp.x = mode.dpp.y = GGI_AUTO; + + mode.graphtype = GT_AUTO; + + drv->state = NULL; + + if (ggiInit() < 0) + return GFX_FATAL; + + meta = (gfx_ggi_struct_t *) sci_calloc(sizeof(gfx_ggi_struct_t), 1); + + if (!(meta->vis = ggiOpen(NULL))) { + DEBUG_BASIC("ggiOpen() failed!\n"); + free(meta); + ggiExit(); + return GFX_FATAL; + } + + while (mode.frames && ggiCheckMode(meta->vis, &mode)) + --(mode.frames); + + if (!mode.frames) { + sciprintf("GFXGGI: Could not find any graphics mode!\n"); + free(meta); + ggiExit(); + return GFX_FATAL; + } + + x_blank = mode.visible.x % 320; + y_blank = mode.visible.y % 200; + mode.visible.x -= x_blank; + mode.visible.y -= y_blank; + + if (mode.visible.x == 0) + mode.visible.x = 320; + + if (mode.visible.y == 0) + mode.visible.y = 200; + + if (GT_DEPTH(mode.graphtype) < 8) + mode.graphtype = GT_8BIT; /* We don't support less than 8 bpp */ + + DEBUG_BASIC("Attempting to create mode with %dx%d, graphtype=%08x, %d frames\n", + mode.visible.x, mode.visible.y, mode.graphtype, mode.frames); + + mode.virt.x = mode.visible.x; + mode.virt.y = mode.visible.y; + mode.size.x = GGI_AUTO; + mode.size.y = GGI_AUTO; + + if (ggiSetMode(meta->vis, &mode)) { + sciprintf("GFXGGI: Could not set proposed graphics mode!\n"); + + mode.virt.x = mode.visible.x += x_blank; + mode.virt.y = mode.visible.y += y_blank; + mode.size.x = GGI_AUTO; + mode.size.y = GGI_AUTO; + + DEBUG_BASIC("Attempting to create augmented mode with %dx%d, graphtype=%08x, %d frames\n", + mode.visible.x, mode.visible.y, mode.graphtype, mode.frames); + + if (ggiSetMode(meta->vis, &mode)) { + sciprintf("GFXGGI: Could not set proposed graphics mode!\n"); + free(meta); + ggiExit(); + return GFX_FATAL; + } + + ggiSetOrigin(meta->vis, (x_blank >> 1), (y_blank >> 1)); + + mode.virt.x = mode.size.x = mode.visible.x -= x_blank; + mode.virt.y = mode.size.y = mode.visible.y -= y_blank; + } else + x_blank = y_blank = 0; + + meta->frames = mode.frames; + + pixelformat = ggiGetPixelFormat(meta->vis); + + drv->mode = gfx_new_mode(mode.visible.x / 320, mode.visible.y / 200, pixelformat->size >> 3, + pixelformat->red_mask, pixelformat->green_mask, pixelformat->blue_mask, + 0, /* alpha mask */ + pixelformat->red_shift, pixelformat->green_shift, pixelformat->blue_shift, + 0, /* alpha shift */ + (GT_SCHEME(mode.graphtype) == GT_PALETTE)? (1 << GT_DEPTH(mode.graphtype)) : 0, 0); + + drv->state = meta; + + meta->priority_maps[GGI_BUFFER_BACK] = + gfx_pixmap_alloc_index_data(gfx_new_pixmap(mode.visible.x, mode.visible.y, GFX_RESID_NONE, 0, 0)); + meta->priority_maps[GGI_BUFFER_STATIC] = + gfx_pixmap_alloc_index_data(gfx_new_pixmap(mode.visible.x, mode.visible.y, GFX_RESID_NONE, 0, 0)); + + meta->priority_maps[GGI_BUFFER_BACK]->flags |= GFX_PIXMAP_FLAG_SCALED_INDEX; + meta->priority_maps[GGI_BUFFER_STATIC]->flags |= GFX_PIXMAP_FLAG_SCALED_INDEX; + + if (_open_meta_visuals(drv)) { + free(meta); + gfx_free_pixmap(drv, meta->priority_maps[GGI_BUFFER_BACK]); + gfx_free_pixmap(drv, meta->priority_maps[GGI_BUFFER_STATIC]); + ggiClose(meta->vis); + ggiExit(); + return GFX_FATAL; + } + + if (meta->frames < 2) { + meta->alt_back_buffer = (byte *) sci_malloc((pixelformat->size >> 3) * mode.visible.x * mode.visible.y); + meta->back_vis = ggiOpen("memory:pointer", meta->alt_back_buffer, NULL); + if (ggiSetSimpleMode(meta->back_vis, mode.visible.x, mode.visible.y, 1, GT_8BIT)) { + sciprintf("GFXGGI: Warning: Setting mode for memory visual failed\n"); + } + } else meta->alt_back_buffer = NULL; + + if (meta->frames < 3) { + meta->static_buffer = (byte *) sci_malloc((pixelformat->size >> 3) * mode.visible.x * mode.visible.y); + meta->static_vis = ggiOpen("memory:pointer", meta->static_buffer, NULL); + if (ggiSetSimpleMode(meta->static_vis, mode.visible.x, mode.visible.y, 1, GT_8BIT)) { + sciprintf("GFXGGI: Warning: Setting mode for memory visual #2 failed\n"); + } + } else meta->static_buffer = NULL; + + init_input_ggi(); + flags = 0; + + STATE->x_blank = x_blank; + STATE->y_blank = y_blank; + STATE->x_blank2 = x_blank >> 1; + STATE->y_blank2 = y_blank >> 1; + + return GFX_OK; +} + + +static void +ggi_exit(gfx_driver_t *drv) +{ + if (drv->state) { + gfx_free_pixmap(drv, STATE->priority_maps[0]); + gfx_free_pixmap(drv, STATE->priority_maps[1]); + + if (STATE->frames < 2) + ggiClose(STATE->back_vis); + + if (STATE->frames < 3) + ggiClose(STATE->static_vis); + + ggiClose(STATE->priority_visuals[0]); + ggiClose(STATE->priority_visuals[1]); + + ggiClose(VISUAL); + } + + gfx_free_mode(drv->mode); + + ggiExit(); + + if (FRAMES < 2) + free(STATE->alt_back_buffer); + + free(drv->state); +} + + +static inline ggi_visual_t +get_writeable_back_visual(gfx_driver_t *drv) +{ + if (STATE->frames > 1) { + ggiSetWriteFrame(VISUAL, 1); + return VISUAL; + } else + return STATE->back_vis; +} + +static inline ggi_visual_t +get_writeable_static_visual(gfx_driver_t *drv) +{ + if (STATE->frames > 2) { + ggiSetWriteFrame(VISUAL, 2); + return VISUAL; + } else + return STATE->static_vis; +} + +static inline ggi_pixel +ggi_map_color(gfx_driver_t *drv, ggi_visual_t vis, gfx_color_t color) +{ + if (MODE->palette) + return (ggi_pixel) color.visual.global_index; + else { + ggi_color gcolor; + gcolor.r = ((int) color.visual.r << 8) | color.visual.r; + gcolor.g = ((int) color.visual.g << 8) | color.visual.g; + gcolor.b = ((int) color.visual.b << 8) | color.visual.b; + gcolor.a = ((int) color.alpha << 8) | color.alpha; + + return ggiMapColor(vis, &gcolor); + } +} + +static int +ggi_draw_filled_rect(gfx_driver_t *drv, rect_t box, gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode); + +static int +ggi_draw_line(gfx_driver_t *drv, point_t start, point_t end, gfx_color_t color, + gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + ggi_pixel pixel; + + int xw = MODE->xfact, yw = MODE->yfact; + int rx, ry, endx, endy; + int xc, yc; + + if (line_mode == GFX_LINE_MODE_FINE) + xw = yw = 1; + else { + xw = MODE->xfact, yw = MODE->yfact; + } + + rx = start.x; + ry = start.y; + endx = end.x; + endy = end.y; + + if ((rx == endx) && (ry == endy)) + return ggi_draw_filled_rect(drv, gfx_rect(rx, ry, xw, yw), color, color, GFX_SHADE_FLAT); + + if (color.mask & GFX_MASK_PRIORITY) { + ggi_visual_t privis = STATE->priority_visuals[GFX_BUFFER_BACK]; + + ggiSetGCForeground(privis, color.priority); + + for (xc = 0; xc < xw; xc++) + ggiDrawLine(privis, rx + xc, ry, endx + xc, endy); + if (yw > 0) + for (xc = 0; xc < xw; xc++) + ggiDrawLine(privis, rx + xc, ry + yw - 1, endx + xc, endy + yw - 1); + + if (yw > 1) { + for (yc = 1; yc < yw-1; yc++) + ggiDrawLine(privis, rx, ry + yc, endx, endy + yc); + + if (xw > 0) + for (yc = 1; yc < yw-1; yc++) + ggiDrawLine(privis, rx + xw - 1, ry + yc, endx + xw - 1, endy + yc); + } + } + + if (color.mask & GFX_MASK_VISUAL) { + ggi_visual_t vis; + + pixel = ggi_map_color(drv, VISUAL, color); + + vis = get_writeable_back_visual(drv); + + ggiSetGCForeground(vis, pixel); + + for (xc = 0; xc < xw; xc++) + ggiDrawLine(vis, rx + xc, ry, endx + xc, endy); + if (yw > 0) + for (xc = 0; xc < xw; xc++) + ggiDrawLine(vis, rx + xc, ry + yw - 1, endx + xc, endy + yw - 1); + + if (yw > 1) { + for (yc = 1; yc < yw-1; yc++) + ggiDrawLine(vis, rx, ry + yc, endx, endy + yc); + + if (xw > 0) + for (yc = 1; yc < yw-1; yc++) + ggiDrawLine(vis, rx + xw - 1, ry + yc, endx + xw - 1, endy + yc); + } + } + + return GFX_OK; +} + +static int +ggi_draw_filled_rect(gfx_driver_t *drv, rect_t box, gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + if (color1.mask & GFX_MASK_VISUAL) { + ggi_pixel pixel = ggi_map_color(drv, VISUAL, color1); + ggi_visual_t vis = get_writeable_back_visual(drv); + + ggiSetGCForeground(vis, pixel); + ggiDrawBox(vis, box.x, box.y, box.xl, box.yl); + } + + if (color1.mask & GFX_MASK_PRIORITY) { + ggi_visual_t vis; + + ggiSetGCForeground(vis = STATE->priority_visuals[GFX_BUFFER_BACK], color1.priority); + ggiDrawBox(vis, box.x, box.y, box.xl, box.yl); + } + + return GFX_OK; +} + +/**************/ +/* Pixmap ops */ +/**************/ + +int +ggi_draw_pixmap(gfx_driver_t *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + ggi_visual_t vis = VISUAL; + const ggi_directbuffer *dbuf; + byte *pri_map = NULL; + + if (dest.xl != src.xl || dest.yl != src.yl) { + GFXERROR("Attempt to scale pixmap (%dx%d)->(%dx%d): Not supported\n", + src.xl, src.yl, dest.xl, dest.yl); + return GFX_ERROR; + } + + switch (buffer) { + + case GFX_BUFFER_FRONT: + GFXERROR("Attempt to draw pixmap to front buffer\n"); + return GFX_ERROR; + + case GFX_BUFFER_BACK: + if (STATE->frames > 1) + ggiSetWriteFrame(VISUAL, 1); + else vis = STATE->back_vis; + pri_map = STATE->priority_maps[GGI_BUFFER_BACK]->index_data; + break; + + + case GFX_BUFFER_STATIC: + if (STATE->frames > 2) + ggiSetWriteFrame(VISUAL, 2); + else vis = STATE->static_vis; + pri_map = STATE->priority_maps[GGI_BUFFER_STATIC]->index_data; + break; + + + default: + GFXERROR("Unexpected buffer ID %d\n", buffer); + return GFX_ERROR; + + } + + assert(pri_map); + assert(vis); + + dbuf = ggiDBGetBuffer(vis, (vis == VISUAL)? buffer : 0); + if (!dbuf) { + GFXERROR("Could not acquire direct buffer!\n"); + return GFX_FATAL; + } + + if (dbuf->resource) { + if (ggiResourceAcquire(dbuf->resource, GGI_ACTYPE_WRITE)) { + GFXERROR("Failed to allocate resource for direct buffer!\n"); + return GFX_FATAL; + } + } + + if (dbuf->layout != blPixelLinearBuffer) { + char *type; + + switch (dbuf->layout) { + case blPixelPlanarBuffer: type = "planar"; + break; + + case blExtended: type = "extended"; + break; + + default: type = "invalid"; + + } + + GFXERROR("Error: Pixel buffer is %s! Non-linear buffers are not supported.\n", type); + + if (dbuf->resource) + ggiResourceRelease(dbuf->resource); + return GFX_FATAL; + } + + gfx_crossblit_pixmap(MODE, pxm, priority, src, dest, (byte *) dbuf->write, + dbuf->buffer.plb.stride, + pri_map, MODE->xfact * 320, 1, 0); + + /* ggiPutBox(vis, dest.x * MODE->xfact, dest.y * MODE->yfact, pxm->xl, pxm->yl, pxm->data); */ + + if (dbuf->resource) + ggiResourceRelease(dbuf->resource); + + return GFX_OK; +} + +int +ggi_grab_pixmap(gfx_driver_t *drv, rect_t src, gfx_pixmap_t *pxm, + gfx_map_mask_t map) +{ + ggi_visual_t vis = VISUAL; + int error; + + pxm->xl = src.xl; + pxm->yl = src.yl; + + if (STATE->frames > 1) + ggiSetReadFrame(VISUAL, 1); + else vis = STATE->back_vis; + + if (src.x < 0 || src.y < 0) { + GFXERROR("Attempt to grab pixmap from invalid coordinates (%d,%d)\n", src.x, src.y); + return GFX_ERROR; + } + + if (!pxm->data) { + GFXERROR("Attempt to grab pixmap to unallocated memory\n"); + return GFX_ERROR; + } + + if ((error = ggiGetBox(vis, src.x, src.y, src.xl, src.yl, pxm->data))) { + GFXERROR("ggiGetBox(%d, %d, %d, %d) returned error code %d\n", src.x, src.y, src.xl, src.yl, error); + return GFX_ERROR; + } + + return GFX_OK; +} + +/************/ +/* Misc ops */ +/************/ + + +static int +ggi_update(gfx_driver_t *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + int sx = src.x, sy = src.y; + int dx = dest.x, dy = dest.y; + int xl = src.xl, yl = src.yl; + + switch (buffer) { + case GFX_BUFFER_FRONT: + ggiSetWriteFrame(VISUAL, 0); + + if (STATE->frames < 2) + ggiCrossBlit(STATE->back_vis, sx, sy, xl, yl, VISUAL, dx + STATE->x_blank2, dy + STATE->y_blank2); + else { + ggiSetReadFrame(VISUAL, 1); + ggiCopyBox(VISUAL, sx, sy, xl, yl, dx + STATE->x_blank2, dy + STATE->y_blank2); + } + + break; + + case GFX_BUFFER_BACK: + if (src.x == dest.x && src.y == dest.y) + gfx_copy_pixmap_box_i(STATE->priority_maps[GGI_BUFFER_BACK], STATE->priority_maps[GGI_BUFFER_STATIC], src); + + if (STATE->frames > 1) + ggiSetWriteFrame(VISUAL, 1); + + if (STATE->frames > 2) { + ggiSetReadFrame(VISUAL, 2); + ggiCopyBox(VISUAL, sx, sy, xl, yl, dx, dy); + return GFX_OK; + } + + ggiCrossBlit(STATE->static_vis, sx, sy, xl, yl, STATE->back_vis, dx, dy); + + break; + + default: + GFXERROR("Invalid buffer %d in update!\n", buffer); + return GFX_ERROR; + } + return GFX_OK; +} + + +static int +ggi_set_palette(gfx_driver_t *drv, int index, byte red, byte green, byte blue) +{ + ggi_color color; + color.r = (red << 8) | red; + color.g = (green << 8) | green; + color.b = (blue << 8) | blue; + + /* DEBUG_POINTER(stderr,"Setting index %d to %04x %04x %04x\n", index, color.r, color.g, color.b); */ + + if (ggiSetPalette(VISUAL, index, 1, &color) < 0) + return GFX_ERROR; + else + return GFX_OK; +} + + +static int +ggi_set_static_buffer(gfx_driver_t *drv, gfx_pixmap_t *pic, gfx_pixmap_t *priority) +{ + ggi_visual_t vis = get_writeable_static_visual(drv); + + /* First, check if the priority map is sane */ + if (priority->index_xl != STATE->priority_maps[GGI_BUFFER_STATIC]->index_xl + || priority->index_yl != STATE->priority_maps[GGI_BUFFER_STATIC]->index_yl) { + GFXERROR("Invalid priority map: (%dx%d) vs expected (%dx%d)\n", + priority->index_xl, priority->index_yl, + STATE->priority_maps[GGI_BUFFER_STATIC]->index_xl, + STATE->priority_maps[GGI_BUFFER_STATIC]->index_yl); + return GFX_ERROR; + } + + ggiPutBox(vis, 0, 0, pic->xl, pic->yl, pic->data); + + memcpy(STATE->priority_maps[GGI_BUFFER_STATIC]->index_data, priority->index_data, + priority->index_xl * priority->index_yl); + + return GFX_OK; +} + + +/********************/ +/* Input management */ +/********************/ + + +struct timeval _sci_ggi_redraw_loopt, _sci_ggi_loopt; +/* timer variables */ + +int _sci_ggi_double_visual; + +static int buckybits; + +#define SCI_TIMEVAL_ADD(timev, addusec) \ + { timev.tv_usec += addusec; \ + while (timev.tv_usec >= 1000000L) { \ + timev.tv_usec -= 1000000L; \ + timev.tv_sec++; \ + }} + +#define SCI_TIMEVAL_LATER(later, earlier) \ + ((later.tv_sec == earlier.tv_sec)? (later.tv_usec >= earlier.tv_usec) \ + : (later.tv_sec >= earlier.tv_sec)) + + +static sci_event_t +ggi_get_event(gfx_driver_t *drv) +{ + struct timeval temptime = {0,0}; + gfx_ggi_struct_t *meta = (gfx_ggi_struct_t *) drv->state; + int modifiers; + + while (1) { + + if (ggiEventPoll(VISUAL, emAll, &temptime)) { + ggi_event event; + sci_event_t retval; + + ggiEventRead(VISUAL, &event, emAll); + + if (flags & SCI_GGI_SWAP_CTRL_CAPS + && ((event.any.type == evKeyPress) + || (event.any.type == evKeyRepeat) + || (event.any.type == evKeyRelease))) { + + switch (event.key.label) { + case GIIK_CtrlL: event.key.label = GIIK_CapsLock; break; + case GIIK_CapsLock: event.key.label = GIIK_CtrlL; break; + } + } + + switch (event.any.type) { + case evKeyPress: + case evKeyRepeat: + retval.type = SCI_EVT_KEYBOARD; + retval.data=-1; + retval.buckybits = 0; + switch(event.key.label) + { + case GIIK_P4: + case GIIK_Left: retval.data=SCI_K_LEFT; retval.buckybits = SCI_EVM_NUMLOCK; break; + case GIIK_P6: + case GIIK_Right: retval.data=SCI_K_RIGHT; retval.buckybits = SCI_EVM_NUMLOCK;break; + case GIIK_P8: + case GIIK_Up: retval.data=SCI_K_UP; retval.buckybits = SCI_EVM_NUMLOCK;break; + case GIIK_P2: + case GIIK_Down: retval.data=SCI_K_DOWN; retval.buckybits = SCI_EVM_NUMLOCK;break; + case GIIK_P7: + case GIIK_Home: retval.data=SCI_K_HOME; retval.buckybits = SCI_EVM_NUMLOCK;break; + case GIIK_P1: + case GIIK_End: retval.data=SCI_K_END; retval.buckybits = SCI_EVM_NUMLOCK;break; + case GIIK_P9: + case GIIK_PageUp: retval.data=SCI_K_PGUP; retval.buckybits = SCI_EVM_NUMLOCK;break; + case GIIK_P3: + case GIIK_PageDown: retval.data=SCI_K_PGDOWN; retval.buckybits = SCI_EVM_NUMLOCK;break; + case GIIK_P5: retval.data=SCI_K_CENTER; retval.buckybits = SCI_EVM_NUMLOCK;break; + + case GIIUC_Minus: + case GIIK_PMinus: retval.data = '-'; break; + case GIIUC_Plus: + case GIIK_PPlus: retval.data = '+'; break; + + case GIIUC_Grave: retval.data = '`'; break; +#if 0 + case GIIK_ShiftL: buckybits |= SCI_EVM_LSHIFT; break; + case GIIK_ShiftR: buckybits |= SCI_EVM_RSHIFT; break; + case GIIK_CtrlR: + case GIIK_CtrlL: buckybits |= SCI_EVM_CTRL; break; + case GIIK_AltL: + case GIIK_AltR: + case GIIK_MetaL: + case GIIK_MetaR: buckybits |= SCI_EVM_ALT; break; + case GIIK_CapsLock: buckybits ^= SCI_EVM_CAPSLOCK; break; + case GIIK_NumLock: buckybits ^= SCI_EVM_NUMLOCK; break; + case GIIK_ScrollLock: buckybits ^= SCI_EVM_SCRLOCK; break; +#endif + case GIIK_Insert: buckybits ^= SCI_EVM_INSERT; break; + case GIIK_PEnter: + case GIIK_Enter: retval.data='\r'; break; + case GIIUC_Tab: retval.data='\t'; break; + case GIIUC_Space: retval.data=' '; break; + case GIIUC_BackSpace: retval.data=SCI_K_BACKSPACE; break; + case GIIK_F1: retval.data = SCI_K_F1; break; + case GIIK_F2: retval.data = SCI_K_F2; break; + case GIIK_F3: retval.data = SCI_K_F3; break; + case GIIK_F4: retval.data = SCI_K_F4; break; + case GIIK_F5: retval.data = SCI_K_F5; break; + case GIIK_F6: retval.data = SCI_K_F6; break; + case GIIK_F7: retval.data = SCI_K_F7; break; + case GIIK_F8: retval.data = SCI_K_F8; break; + case GIIK_F9: retval.data = SCI_K_F9; break; + case GIIK_F10: retval.data = SCI_K_F10; break; + case GIIUC_Escape: retval.data = SCI_K_ESC; break; + + /*FIXME: Add all special keys in a sane way*/ + default: + { + if(event.key.label>='a' && event.key.label<='z') + retval.data=event.key.label-'a'+97; + if(event.key.label>='A' && event.key.label<='Z') + retval.data=event.key.label-'A'+97; + if(event.key.label>='0' && event.key.label<='9') + retval.data=event.key.label-'0'+48; + } + } + + modifiers = event.key.modifiers; + + buckybits = (buckybits & SCI_EVM_INSERT) | + (((modifiers & GII_MOD_CAPS)? SCI_EVM_LSHIFT | SCI_EVM_RSHIFT : 0) + | ((modifiers & GII_MOD_CTRL)? SCI_EVM_CTRL : 0) + | ((modifiers & (GII_MOD_ALT | GII_MOD_META))? SCI_EVM_ALT : 0) + | ((modifiers & GII_MOD_NUM)? SCI_EVM_NUMLOCK : 0) + | ((modifiers & GII_MOD_SCROLL)? SCI_EVM_SCRLOCK : 0)) + ^ ((modifiers & GII_MOD_SHIFT)? SCI_EVM_LSHIFT | SCI_EVM_RSHIFT : 0); + + if(retval.data==-1) continue; + retval.buckybits |= buckybits; + + return retval; + + case evKeyRelease: +#if 0 + switch(event.key.label) + { + case GIIK_ShiftL: buckybits &= ~SCI_EVM_LSHIFT; break; + case GIIK_ShiftR: buckybits &= ~SCI_EVM_RSHIFT; break; + case GIIK_CtrlR: + case GIIK_CtrlL: buckybits &= ~SCI_EVM_CTRL; break; + case GIIK_AltL: + case GIIK_AltR: + case GIIK_MetaL: + case GIIK_MetaR: buckybits &= ~SCI_EVM_ALT; break; + } +#endif + continue; + + case evPtrButtonPress: + retval.type = SCI_EVT_MOUSE_PRESS; + retval.data = event.pbutton.button; + retval.buckybits=buckybits; + + if (event.pbutton.button == GII_PBUTTON_LEFT) + retval.buckybits |= SCI_EVM_CTRL; + + if (event.pbutton.button == GII_PBUTTON_RIGHT) + retval.buckybits |= SCI_EVM_LSHIFT | SCI_EVM_RSHIFT; + return retval; + + case evPtrButtonRelease: + retval.type = SCI_EVT_MOUSE_RELEASE; + retval.data = event.pbutton.button; + retval.buckybits=buckybits; + + if (event.pbutton.button == GII_PBUTTON_LEFT) + retval.buckybits |= SCI_EVM_CTRL; + + if (event.pbutton.button == GII_PBUTTON_RIGHT) + retval.buckybits |= SCI_EVM_LSHIFT | SCI_EVM_RSHIFT; + + return retval; + + case evPtrAbsolute: + drv->pointer_x = event.pmove.x - STATE->x_blank2; + drv->pointer_y = event.pmove.y - STATE->y_blank2; + continue; + + case evPtrRelative: + drv->pointer_x += event.pmove.x; + drv->pointer_y += event.pmove.y; + /* FIXME: This may make the pointer too fast on high res! */ + continue; + } + } else { + sci_event_t retval; + + retval.type = SCI_EVT_NONE; /* Nothing happened */ + return retval; + } + + } +} + +static void +init_input_ggi() +{ + _sci_ggi_loopt = _sci_ggi_redraw_loopt; + buckybits = SCI_EVM_INSERT; /* Start up in "insert" mode */ + /* reset timers, leave them at current time to send redraw events ASAP */ + +} + + +int +ggi_usleep(gfx_driver_t* drv, long usec) +{ + struct timeval tv = {0, usec}; + + while(tv.tv_usec>0) + { + if(ggiEventPoll(VISUAL, emPtrMove, &tv)) + { + ggi_event e; + ggiEventRead(VISUAL, &e, emPtrMove); + switch(e.any.type) + { + case evPtrRelative: + { + drv->pointer_x+=e.pmove.x; + drv->pointer_y+=e.pmove.y; + } return GFX_OK; + case evPtrAbsolute: + { + drv->pointer_x=e.pmove.x - STATE->x_blank2; + drv->pointer_y=e.pmove.y - STATE->y_blank2; + } return GFX_OK; + } + } + } + return GFX_OK; +} + + + + +gfx_driver_t gfx_driver_ggi = { + "ggi", + GGI_DRIVER_VERSION, + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, + 0,0, + GFX_CAPABILITY_FINE_LINES, GFX_DEBUG_POINTER + | GFX_DEBUG_UPDATES | GFX_DEBUG_PIXMAPS | GFX_DEBUG_BASIC, + ggi_set_param, + ggi_init_specific, + ggi_init, + ggi_exit, + ggi_draw_line, + ggi_draw_filled_rect, + NULL, + NULL, + ggi_draw_pixmap, + ggi_grab_pixmap, + ggi_update, + ggi_set_static_buffer, + NULL, + ggi_set_palette, + ggi_get_event, + ggi_usleep +}; + + +#endif /* HAVE_LIBGGI */ diff --git a/engines/sci/gfx/drivers/null_driver.c b/engines/sci/gfx/drivers/null_driver.c new file mode 100644 index 0000000000..e6821f56ef --- /dev/null +++ b/engines/sci/gfx/drivers/null_driver.c @@ -0,0 +1,211 @@ +/*************************************************************************** + null_driver.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include + +static int debug_sleep = 0; +static int debug_draw = 0; + +static int +null_set_parameter(struct _gfx_driver *drv, char *attribute, char *value) +{ + printf("[GFX-NULL] Setting '%s' <- '%s'\n", attribute, value); + + return GFX_OK; +} + + + +static int +null_init_specific(struct _gfx_driver *drv, int xfact, int yfact, int bytespp) +{ + printf("[GFX-NULL] Initializing specific: %dx%d, %d bytespp\n", + xfact, yfact, bytespp); + + drv->mode = gfx_new_mode(xfact, yfact, bytespp, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0); + + return GFX_OK; +} + + +static int +null_init(struct _gfx_driver *drv) +{ + printf("[GFX-NULL] Initializing default\n"); + return null_init_specific(drv, 1, 1, 1); +} + +static void +null_exit(struct _gfx_driver *drv) +{ + printf("[GFX-NULL] Exitting\n"); +} + + + /*** Drawing operations ***/ + +static int +null_draw_line(struct _gfx_driver *drv, point_t start, point_t end, + gfx_color_t color, + gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + if (debug_draw) + printf("[GFX-NULL] Line (%d,%d) -- (%d,%d)\n", + GFX_PRINT_POINT(start), + GFX_PRINT_POINT(end)); + return GFX_OK; +} + +static int +null_draw_filled_rect(struct _gfx_driver *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + if (debug_draw) + printf("[GFX-NULL] Box (%d,%d)d(%d,%d)\n", + GFX_PRINT_RECT(rect)); + return GFX_OK; +} + + + /*** Pixmap operations ***/ + +static int +null_register_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + return GFX_OK; +} + +static int +null_unregister_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + return GFX_OK; +} + +static int +null_draw_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + return GFX_OK; +} + +static int +null_grab_pixmap(struct _gfx_driver *drv, rect_t src, gfx_pixmap_t *pxm, + gfx_map_mask_t map) +{ + return GFX_OK; + pxm->xl = src.xl; + pxm->yl = src.yl; +} + + + /*** Buffer operations ***/ + +static int +null_update(struct _gfx_driver *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + return GFX_OK; +} + +static int +null_set_static_buffer(struct _gfx_driver *drv, gfx_pixmap_t *pic, gfx_pixmap_t *priority) +{ + return GFX_OK; +} + + + /*** Mouse pointer operations ***/ + + +static int +null_set_pointer(struct _gfx_driver *drv, gfx_pixmap_t *pointer) +{ + return GFX_OK; +} + + + /*** Palette operations ***/ + +static int +null_set_palette(struct _gfx_driver *drv, int index, byte red, byte green, byte blue) +{ + return GFX_OK; +} + + + /*** Event management ***/ + +static sci_event_t +null_get_event(struct _gfx_driver *drv) +{ + sci_event_t input; + + input.type = SCI_EVT_NONE; + + return input; +} + + +static int +null_usec_sleep(struct _gfx_driver *drv, long usecs) +{ + if (debug_sleep) + sciprintf("[GFX-NULL] Sleeping %ld usecs...\n", usecs); + return GFX_OK; +} + +gfx_driver_t +gfx_driver_null = { + "null", + "0.1", + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, + 0, 0, + GFX_CAPABILITY_WINDOWED, + GFX_DEBUG_POINTER | GFX_DEBUG_UPDATES | GFX_DEBUG_PIXMAPS | GFX_DEBUG_BASIC, + null_set_parameter, + null_init_specific, + null_init, + null_exit, + null_draw_line, + null_draw_filled_rect, + null_register_pixmap, + null_unregister_pixmap, + null_draw_pixmap, + null_grab_pixmap, + null_update, + null_set_static_buffer, + null_set_pointer, + null_set_palette, + null_get_event, + null_usec_sleep, + NULL +}; diff --git a/engines/sci/gfx/drivers/scummvm_driver.cpp b/engines/sci/gfx/drivers/scummvm_driver.cpp new file mode 100644 index 0000000000..dc38cb58e4 --- /dev/null +++ b/engines/sci/gfx/drivers/scummvm_driver.cpp @@ -0,0 +1,543 @@ +#include +#include +#include + +#include "gfx_driver.h" +#include "gfx_tools.h" + +struct _scummvm_driver_state { + gfx_pixmap_t *priority[2]; + byte *visual[3]; + byte *pointer_data[2]; + int xsize, ysize; + //int buckystate; + bool update_screen; + bool update_mouse; +}; + +#define S ((struct _scummvm_driver_state *)(drv->state)) + +static int +scummvm_init_specific(struct _gfx_driver *drv, int xfact, int yfact, int bytespp) +{ + int i; + + if (!drv->state) // = S + drv->state = new _scummvm_driver_state; + if (!drv->state) + return GFX_FATAL; + + S->xsize = xfact * 320; + S->ysize = yfact * 200; + + S->pointer_data[0] = NULL; + S->pointer_data[1] = NULL; + //S->buckystate = 0; + + for (i = 0; i < 2; i++) { + S->priority[i] = gfx_pixmap_alloc_index_data(gfx_new_pixmap(S->xsize, S->ysize, GFX_RESID_NONE, -i, -777)); + if (!S->priority[i]) { + printf("Out of memory: Could not allocate priority maps! (%dx%d)\n", + S->xsize, S->ysize); + return GFX_FATAL; + } + } + // create the visual buffers + for (i = 0; i < 3; i++) { + S->visual[i] = NULL; + S->visual[i] = new byte[S->xsize * S->ysize]; + if (!S->visual[i]) { + printf("Out of memory: Could not allocate visual buffers! (%dx%d)\n", + S->xsize, S->ysize); + return GFX_FATAL; + } + memset(S->visual[i], 0, S->xsize * S->ysize); + } + + drv->mode = gfx_new_mode(xfact, yfact, bytespp, + 0, 0, 0, 0, + 0, 0, 0, 0, 256, 0); + + return GFX_OK; +} + +static int +scummvm_init(struct _gfx_driver *drv) +{ + return scummvm_init_specific(drv, 1, 1, GFX_COLOR_MODE_INDEX); +} + +static void +scummvm_exit(struct _gfx_driver *drv) +{ + int i; + if (S) { + for (i = 0; i < 2; i++) { + gfx_free_pixmap(drv, S->priority[i]); + S->priority[i] = NULL; + } + + for (i = 0; i < 3; i++) { + delete[] S->visual[i]; + S->visual[i] = NULL; + } + + for (i = 0; i < 2; i++) + if (S->pointer_data[i]) { + delete[] S->pointer_data[i]; + S->pointer_data[i] = NULL; + } + + delete S; + } +} + + +// Drawing operations + +/* This code shamelessly lifted from the SDL_gfxPrimitives package */ +static void +lineColor2(byte *dst, int16 x1, int16 y1, int16 x2, int16 y2, uint32 color) +{ + int pixx, pixy; + int x,y; + int dx,dy; + int sx,sy; + int swaptmp; + uint8 *pixel; + + dx = x2 - x1; + dy = y2 - y1; + sx = (dx >= 0) ? 1 : -1; + sy = (dy >= 0) ? 1 : -1; + + dx = sx * dx + 1; + dy = sy * dy + 1; + pixx = 1; + pixy = 320; + pixel = ((uint8*)dst) + pixx * (int)x1 + pixy * (int)y1; + pixx *= sx; + pixy *= sy; + if (dx < dy) { + swaptmp = dx; dx = dy; dy = swaptmp; + swaptmp = pixx; pixx = pixy; pixy = swaptmp; + } + + /* Draw */ + x=0; + y=0; + for(; x < dx; x++, pixel += pixx) { + *pixel = color; + y += dy; + if (y >= dx) { + y -= dx; pixel += pixy; + } + } +} + +static int +scummvm_draw_line(struct _gfx_driver *drv, point_t start, point_t end, + gfx_color_t color, + gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + uint32 scolor = color.visual.global_index; + int xsize = S->xsize; + int ysize = S->ysize; + + if (color.mask & GFX_MASK_VISUAL) { + point_t nstart, nend; + + nstart.x = start.x; + nstart.y = start.y; + nend.x = end.x; + nend.y = end.y; + + if (nstart.x < 0) + nstart.x = 0; + if (nend.x < 0) + nstart.x = 0; + if (nstart.y < 0) + nstart.y = 0; + if (nend.y < 0) + nend.y = 0; + if (nstart.x > xsize) + nstart.x = xsize; + if (nend.x >= xsize) + nend.x = xsize -1; + if (nstart.y > ysize) + nstart.y = ysize; + if (nend.y >= ysize) + nend.y = ysize -1; + + lineColor2(S->visual[1], (int16)nstart.x, (int16)nstart.y, + (int16)nend.x, (int16)nend.y, scolor); + + if (color.mask & GFX_MASK_PRIORITY) { + gfx_draw_line_pixmap_i(S->priority[0], nstart, nend, + color.priority); + } + } + + return GFX_OK; +} + +static int +scummvm_draw_filled_rect(struct _gfx_driver *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + if (color1.mask & GFX_MASK_VISUAL) { + for (int i = rect.y; i < rect.y + rect.yl; i++) { + memset(S->visual[1] + i * S->xsize + rect.x, color1.visual.global_index, rect.xl); + } + } + + if (color1.mask & GFX_MASK_PRIORITY) + gfx_draw_box_pixmap_i(S->priority[0], rect, color1.priority); + + return GFX_OK; +} + + +// Pixmap operations + +static int +scummvm_draw_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + int bufnr = (buffer == GFX_BUFFER_STATIC)? 2:1; + int pribufnr = bufnr -1; + + if (dest.xl != src.xl || dest.yl != src.yl) { + printf("Attempt to scale pixmap (%dx%d)->(%dx%d): Not supported\n", + src.xl, src.yl, dest.xl, dest.yl); + return GFX_ERROR; + } + + gfx_crossblit_pixmap(drv->mode, pxm, priority, src, dest, + S->visual[bufnr], S->xsize, + S->priority[pribufnr]->index_data, + S->priority[pribufnr]->index_xl, 1, 0); + + return GFX_OK; +} + +static int +scummvm_grab_pixmap(struct _gfx_driver *drv, rect_t src, gfx_pixmap_t *pxm, + gfx_map_mask_t map) +{ + if (src.x < 0 || src.y < 0) { + printf("Attempt to grab pixmap from invalid coordinates (%d,%d)\n", src.x, src.y); + return GFX_ERROR; + } + + if (!pxm->data) { + printf("Attempt to grab pixmap to unallocated memory\n"); + return GFX_ERROR; + } + + switch (map) { + + case GFX_MASK_VISUAL: + pxm->xl = src.xl; + pxm->yl = src.yl; + for (int i = 0; i < src.yl; i++) { + memcpy(pxm->data + i * src.xl, S->visual[1] + (i + src.y) * S->xsize + src.x, src.xl); + } + break; + + case GFX_MASK_PRIORITY: + printf("FIXME: priority map grab not implemented yet!\n"); + break; + + default: + printf("Attempt to grab pixmap from invalid map 0x%02x\n", map); + return GFX_ERROR; + } + + return GFX_OK; +} + + +// Buffer operations + +static int +scummvm_update(struct _gfx_driver *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + //TODO + int data_source = (buffer == GFX_BUFFER_BACK)? 2 : 1; + int data_dest = data_source - 1; + + /* + if (src.x != dest.x || src.y != dest.y) { + printf("Updating %d (%d,%d)(%dx%d) to (%d,%d) on %d\n", buffer, src.x, src.y, + src.xl, src.yl, dest.x, dest.y, data_dest); + } else { + printf("Updating %d (%d,%d)(%dx%d) to %d\n", buffer, src.x, src.y, src.xl, src.yl, data_dest); + } + */ + + switch (buffer) { + case GFX_BUFFER_BACK: + //memcpy(S->visual[data_dest], S->visual[data_source], + // S->xsize * S->ysize); + for (int i = 0; i < src.yl; i++) { + memcpy(S->visual[data_dest] + (dest.y + i) * S->xsize + dest.x, + S->visual[data_source] + (src.y + i) * S->xsize + src.x, src.xl); + } + + if ((src.x == dest.x) && (src.y == dest.y)) + gfx_copy_pixmap_box_i(S->priority[0], S->priority[1], src); + break; + case GFX_BUFFER_FRONT: + memcpy(S->visual[data_dest], S->visual[data_source], + S->xsize * S->ysize); + + g_system->copyRectToScreen(S->visual[data_dest] + src.x + src.y * S->xsize, + S->xsize, dest.x, dest.y, src.xl, src.yl); + /* + g_system->copyRectToScreen(S->visual[data_dest], + S->xsize, 0, 0, S->xsize, S->ysize); + */ + S->update_screen = true; + break; + default: + GFXERROR("Invalid buffer %d in update!\n", buffer); + return GFX_ERROR; + } + + + return GFX_OK; +} + +static int +scummvm_set_static_buffer(struct _gfx_driver *drv, gfx_pixmap_t *pic, gfx_pixmap_t *priority) +{ + memcpy(S->visual[2], pic->data, S->xsize * S->ysize); + /*gfx_crossblit_pixmap(drv->mode, pic, 0, rect, rect, + S->visual[2], S->xsize, + S->priority[1]->index_data, + S->priority[1]->index_xl, 1, 0);*/ + + gfx_copy_pixmap_box_i(S->priority[1], priority, gfx_rect(0, 0, S->xsize, S->ysize)); + + return GFX_OK; +} + + +// Mouse pointer operations + +static int +scummvm_set_pointer(struct _gfx_driver *drv, gfx_pixmap_t *pointer) +{ + if (pointer == NULL) { + g_system->showMouse(false); + } else { + g_system->setMouseCursor(pointer->index_data, pointer->xl, pointer->yl, pointer->xoffset, pointer->yoffset); + g_system->showMouse(true); + } + + // Pointer pixmap or mouse position has changed + S->update_mouse = true; + return GFX_OK; +} + + +// Palette operations + +static int +scummvm_set_palette(struct _gfx_driver *drv, int index, byte red, byte green, byte blue) +{ + byte color[] = {red, green, blue, 255}; + g_system->setPalette(color, index, 1); + return GFX_OK; +} + + +// Event management + +static sci_event_t +scummvm_get_event(struct _gfx_driver *drv) +{ + sci_event_t input; + input.type = SCI_EVT_NONE; + + Common::EventManager *em = g_system->getEventManager(); + Common::Event ev; + + bool found = em->pollEvent(ev); + Common::Point p = ev.mouse; + + // Don't generate events for mouse movement + while (found && ev.type == Common::EVENT_MOUSEMOVE) { + found = em->pollEvent(ev); + p = ev.mouse; + drv->pointer_x = p.x; + drv->pointer_y = p.y; + S->update_mouse = true; + } + + // Update the screen here, since it's called very often + if (S->update_mouse) + g_system->warpMouse(drv->pointer_x, drv->pointer_y); + if (S->update_screen || S->update_mouse) { + g_system->updateScreen(); + S->update_screen = false; + S->update_mouse = false; + } + + if (found && !ev.synthetic && ev.type != Common::EVENT_MOUSEMOVE) { + int modifiers; + if (ev.type == Common::EVENT_KEYDOWN) + modifiers = ev.kbd.flags; + else + modifiers = em->getModifierState(); + + input.buckybits = + ((modifiers & Common::KBD_ALT) ? SCI_EVM_ALT : 0) | + ((modifiers & Common::KBD_CTRL) ? SCI_EVM_CTRL : 0) | + ((modifiers & Common::KBD_SHIFT) ? SCI_EVM_LSHIFT | SCI_EVM_RSHIFT : 0); + //TODO: SCI_EVM_SCRLOCK SCI_EVM_NUMLOCK SCI_EVM_CAPSLOCK SCI_EVM_INSERT + + switch (ev.type) { + // Keyboard events + case Common::EVENT_KEYDOWN: + input.data = ev.kbd.keycode; + input.character = ev.kbd.ascii; + + if (!(input.data & 0xFF00)) { + // Directly accept most common keys without conversion + input.type = SCI_EVT_KEYBOARD; + if (input.data == Common::KEYCODE_TAB) { + // Tab + input.type = SCI_EVT_KEYBOARD; + input.data = SCI_K_TAB; + if (input.buckybits & (SCI_EVM_LSHIFT | SCI_EVM_RSHIFT)) + input.character = SCI_K_SHIFT_TAB; + else + input.character = SCI_K_TAB; + } + } else if ((input.data >= Common::KEYCODE_F1) && input.data <= Common::KEYCODE_F10) { + // F1-F10 + input.type = SCI_EVT_KEYBOARD; + // SCI_K_F1 == 59 << 8 + // SCI_K_SHIFT_F1 == 84 << 8 + input.data = (input.data - Common::KEYCODE_F1 + SCI_K_F1) << 8; + if (input.buckybits & (SCI_EVM_LSHIFT | SCI_EVM_RSHIFT)) + input.character = input.data + ((SCI_K_SHIFT_F1 - SCI_K_F1) << 8); + else + input.character = input.data; + } else { + // Special keys that need conversion + input.type = SCI_EVT_KEYBOARD; + switch (ev.kbd.keycode) { + case Common::KEYCODE_UP: + input.data = SCI_K_UP; + break; + case Common::KEYCODE_DOWN: + input.data = SCI_K_DOWN; + break; + case Common::KEYCODE_RIGHT: + input.data = SCI_K_RIGHT; + break; + case Common::KEYCODE_LEFT: + input.data = SCI_K_LEFT; + break; + case Common::KEYCODE_INSERT: + input.data = SCI_K_INSERT; + break; + case Common::KEYCODE_HOME: + input.data = SCI_K_HOME; + break; + case Common::KEYCODE_END: + input.data = SCI_K_END; + break; + case Common::KEYCODE_PAGEUP: + input.data = SCI_K_PGUP; + break; + case Common::KEYCODE_PAGEDOWN: + input.data = SCI_K_PGDOWN; + break; + case Common::KEYCODE_DELETE: + input.data = SCI_K_DELETE; + break; + //TODO: SCI_K_CENTER + default: + input.type = SCI_EVT_NONE; + break; + } + input.character = input.data; + } + break; + + // Mouse events + case Common::EVENT_LBUTTONDOWN: + input.type = SCI_EVT_MOUSE_PRESS; + input.data = 1; + drv->pointer_x = p.x; + drv->pointer_y = p.y; + break; + case Common::EVENT_RBUTTONDOWN: + input.type = SCI_EVT_MOUSE_PRESS; + input.data = 2; + drv->pointer_x = p.x; + drv->pointer_y = p.y; + break; + case Common::EVENT_LBUTTONUP: + input.type = SCI_EVT_MOUSE_RELEASE; + input.data = 1; + drv->pointer_x = p.x; + drv->pointer_y = p.y; + break; + case Common::EVENT_RBUTTONUP: + input.type = SCI_EVT_MOUSE_RELEASE; + input.data = 2; + drv->pointer_x = p.x; + drv->pointer_y = p.y; + break; + + // Misc events + case Common::EVENT_QUIT: + input.type = SCI_EVT_QUIT; + break; + } + } + + return input; +} + +static int +scummvm_usec_sleep(struct _gfx_driver *drv, long usecs) +{ + g_system->delayMillis(usecs/1000); + return GFX_OK; +} + +gfx_driver_t +gfx_driver_scummvm = { + "ScummVM", + "0.1", + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, + 0, 0, + GFX_CAPABILITY_MOUSE_POINTER | GFX_CAPABILITY_COLOR_MOUSE_POINTER | GFX_CAPABILITY_MOUSE_SUPPORT | GFX_CAPABILITY_FINE_LINES | GFX_CAPABILITY_WINDOWED, + 0, + NULL, + scummvm_init_specific, + scummvm_init, + scummvm_exit, + scummvm_draw_line, + scummvm_draw_filled_rect, + NULL, + NULL, + scummvm_draw_pixmap, + scummvm_grab_pixmap, + scummvm_update, + scummvm_set_static_buffer, + scummvm_set_pointer, + scummvm_set_palette, + scummvm_get_event, + scummvm_usec_sleep, + NULL +}; diff --git a/engines/sci/gfx/drivers/sdl_driver.c b/engines/sci/gfx/drivers/sdl_driver.c new file mode 100644 index 0000000000..e52cea9db5 --- /dev/null +++ b/engines/sci/gfx/drivers/sdl_driver.c @@ -0,0 +1,1235 @@ +/*************************************************************************** + sdl_driver.c Copyright (C) 2001 Solomon Peachy + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + Solomon Peachy + +***************************************************************************/ + +/* set optimisations for Win32: */ +/* g on: enable global optimizations */ +/* t on: use fast code */ +/* y on: suppress creation of frame pointers on stack */ +/* s off: disable minimize size code */ +#ifdef _WIN32 +# include +# ifndef SATISFY_PURIFY +# pragma optimize( "s", off ) +# pragma optimize( "gty", on ) +# pragma intrinsic( memcpy, memset ) +# endif +#endif + +#include + +#include +#ifdef HAVE_SDL +#include + +#if !defined(_MSC_VER) +# include +#endif + +#include +#undef HAVE_ICONV +#undef HAVE_ICONV_H +#undef HAVE_ALLOCA_H + +#include + +#ifndef SDL_DISABLE +# define SDL_DISABLE 0 +#endif +#ifndef SDL_ALPHA_OPAQUE +# define SDL_ALPHA_OPAQUE 255 +#endif + +#define SCI_SDL_HANDLE_NORMAL 0 +#define SCI_SDL_HANDLE_GRABBED 1 + +#define SCI_SDL_SWAP_CTRL_CAPS (1 << 0) +#define SCI_SDL_FULLSCREEN (1 << 2) + +static int +sdl_usec_sleep(struct _gfx_driver *drv, long usecs); + +static int flags = 0; + +struct _sdl_state { + int used_bytespp; + gfx_pixmap_t *priority[2]; + SDL_Color colors[256]; + SDL_Surface *visual[3]; + SDL_Surface *primary; + int buckystate; + byte *pointer_data[2]; + int alpha_mask; + int SDL_alpha_shift; + int SDL_alpha_loss; +}; + +#define S ((struct _sdl_state *)(drv->state)) + +#define XFACT drv->mode->xfact +#define YFACT drv->mode->yfact + +#define DEBUGB if (drv->debug_flags & GFX_DEBUG_BASIC && ((debugline = __LINE__))) sdlprintf +#define DEBUGU if (drv->debug_flags & GFX_DEBUG_UPDATES && ((debugline = __LINE__))) sdlprintf +#define DEBUGPXM if (drv->debug_flags & GFX_DEBUG_PIXMAPS && ((debugline = __LINE__))) sdlprintf +#define DEBUGPTR if (drv->debug_flags & GFX_DEBUG_POINTER && ((debugline = __LINE__))) sdlprintf +#define SDLERROR if ((debugline = __LINE__)) sdlprintf +#define SDLPRINTF if ((debugline = __LINE__)) sdlprintf + +#define ALPHASURFACE (S->used_bytespp == 4) + +static int debugline = 0; + +static void +sdlprintf(const char *fmt, ...) +{ + va_list argp; + fprintf(stderr,"GFX-SDL %d:", debugline); + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); +} + +static int +sdl_init_libsdl(struct _gfx_driver *drv) +{ + if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE)) { + DEBUGB("Failed to init SDL\n"); + return -1; + } + + SDL_EnableUNICODE(SDL_ENABLE); + + return 0; +} + +static int +sdl_alloc_primary(struct _gfx_driver *drv, int xfact, int yfact, int bytespp) +{ + int i = SDL_HWSURFACE | SDL_HWPALETTE; + + if (flags & SCI_SDL_FULLSCREEN) { + i |= SDL_FULLSCREEN; + } + + S->primary = SDL_SetVideoMode(xfact * 320, yfact * 200, bytespp << 3, i); + + if (!S->primary) { + SDLERROR("Could not set up a primary SDL surface!\n"); + return -1; + } + + if (S->primary->format->BytesPerPixel != bytespp) { + SDLERROR("Could not set up a primary SDL surface of depth %d bpp!\n",bytespp); + S->primary = NULL; + return -1; + } + + /* Set windowed flag */ + if (S->primary->flags & SDL_FULLSCREEN) + drv->capabilities &= ~GFX_CAPABILITY_WINDOWED; + else + drv->capabilities |= GFX_CAPABILITY_WINDOWED; + + return 0; +} + +static int +sdl_blit_surface(gfx_driver_t *drv, + SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect) +{ + if (S->used_bytespp == 1) { + SDL_SetColors(src, S->colors, 0, 256); + SDL_SetColors(dst, S->colors, 0, 256); + } + return SDL_BlitSurface(src, srcrect, dst, dstrect); +} + +static int +sdl_set_parameter(struct _gfx_driver *drv, char *attribute, char *value) +{ + if (!strncmp(attribute, "swap_ctrl_caps", 14) || + !strncmp(attribute, "swap_caps_ctrl", 14)) { + if (string_truep(value)) + flags |= SCI_SDL_SWAP_CTRL_CAPS; + else + flags &= ~SCI_SDL_SWAP_CTRL_CAPS; + return GFX_OK; + } + + if (!strncmp(attribute, "fullscreen", 10)) { + if (string_truep(value)) + flags |= SCI_SDL_FULLSCREEN; + else + flags &= ~SCI_SDL_FULLSCREEN; + + return GFX_OK; + } + + + SDLERROR("Attempt to set sdl parameter \"%s\" to \"%s\"\n", attribute, value); + return GFX_ERROR; +} + +static int +sdl_init_specific(struct _gfx_driver *drv, int xfact, int yfact, int bytespp) +{ + int red_shift, green_shift, blue_shift, alpha_shift; + int xsize = xfact * 320; + int ysize = yfact * 200; + + int i; + +#ifdef _MSC_VER /* Win32 doesn't support mouse pointers greater than 64x64 */ + if (xfact > 2 || yfact > 2) + drv->capabilities &= ~GFX_CAPABILITY_MOUSE_POINTER; +#endif +#if defined(__BEOS__) || defined(__amigaos4__) /* BeOS has been reported not to work well with the mouse pointer at all */ + drv->capabilities &= ~GFX_CAPABILITY_MOUSE_POINTER; +#endif + + if (sdl_init_libsdl(drv)) + return GFX_FATAL; + + if (!drv->state /* = S */) + drv->state = sci_malloc(sizeof(struct _sdl_state)); + if (!drv->state) + return GFX_FATAL; + + if (xfact < 1 || yfact < 1 || bytespp < 1 || bytespp > 4) { + SDLERROR("Internal error: Attempt to open window w/ scale factors (%d,%d) and bpp=%d!\n", + xfact, yfact, bytespp); + } + + if (sdl_alloc_primary(drv, xfact, yfact, bytespp)) + return GFX_FATAL; + + S->used_bytespp = bytespp; + + printf("Using primary SDL surface of %d,%d @%d bpp\n", + xsize, ysize, bytespp << 3); + + /* if (S->primary->format->BytesPerPixel == 4) { + S->alpha_mask = 0xff000000; + S->SDL_alpha_shift = 24; + S->SDL_alpha_loss = 0; + alpha_shift = 0; + } else { */ + S->alpha_mask = S->primary->format->Amask; + S->SDL_alpha_shift = S->primary->format->Ashift; + S->SDL_alpha_loss = S->primary->format->Aloss; + alpha_shift = bytespp << 3; + /* }*/ + + /* clear palette */ + for (i = 0; i < 256; i++) { + S->colors[i].r = (i & 1)? 0 : 0; + S->colors[i].g = (i & 2)? 0 : 0; + S->colors[i].b = (i & 4)? 0 : 0; + } + if (bytespp == 1) + SDL_SetColors(S->primary, S->colors, 0, 256); + + /* create an input event mask */ + SDL_EventState(SDL_ACTIVEEVENT, SDL_IGNORE); + SDL_EventState(SDL_VIDEORESIZE, SDL_IGNORE); + SDL_EventState(SDL_KEYUP, SDL_IGNORE); + + SDL_WM_SetCaption("FreeSCI", "freesci"); + + SDL_ShowCursor(SDL_DISABLE); + S->pointer_data[0] = NULL; + S->pointer_data[1] = NULL; + + S->buckystate = 0; + + if (bytespp == 1) { + red_shift = green_shift = blue_shift = alpha_shift = 0; + } else { + red_shift = 24 - S->primary->format->Rshift + S->primary->format->Rloss; + green_shift = 24 - S->primary->format->Gshift + S->primary->format->Gloss; + blue_shift = 24 - S->primary->format->Bshift + S->primary->format->Bloss; + } + + printf("%08x %08x %08x %08x %d/%d=%d %d/%d=%d %d/%d=%d %d/%d=%d\n", + S->primary->format->Rmask, + S->primary->format->Gmask, + S->primary->format->Bmask, + S->alpha_mask, + /* S->primary->format->Amask,*/ + S->primary->format->Rshift, + S->primary->format->Rloss, + red_shift, + S->primary->format->Gshift, + S->primary->format->Gloss, + green_shift, + S->primary->format->Bshift, + S->primary->format->Bloss, + blue_shift, + S->SDL_alpha_shift, + S->SDL_alpha_loss, + /* + S->primary->format->Ashift, + S->primary->format->Aloss, */ + alpha_shift); + + for (i = 0; i < 2; i++) { + S->priority[i] = gfx_pixmap_alloc_index_data(gfx_new_pixmap(xsize, ysize, GFX_RESID_NONE, -i, -777)); + if (!S->priority[i]) { + SDLERROR("Out of memory: Could not allocate priority maps! (%dx%d)\n", + xsize, ysize); + return GFX_FATAL; + } + } + + /* create the visual buffers */ + for (i = 0; i < 3; i++) { + S->visual[i] = SDL_CreateRGBSurface(SDL_SRCALPHA, + /* SDL_HWSURFACE | SDL_SWSURFACE, */ + xsize, ysize, + bytespp << 3, + S->primary->format->Rmask, + S->primary->format->Gmask, + S->primary->format->Bmask, + S->alpha_mask); + if (S->visual[i] == NULL) { + SDLERROR("Could not set up visual buffers!\n"); + return GFX_FATAL; + } + + if (ALPHASURFACE) + SDL_SetAlpha(S->visual[i],SDL_SRCALPHA,SDL_ALPHA_OPAQUE); + + if (SDL_FillRect(S->primary, NULL, SDL_MapRGB(S->primary->format, 0,0,0))) + SDLERROR("Couldn't fill backbuffer!\n"); + } + + SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); + + drv->mode = gfx_new_mode(xfact, yfact, bytespp, + S->primary->format->Rmask, + S->primary->format->Gmask, + S->primary->format->Bmask, + S->alpha_mask, + red_shift, green_shift, blue_shift, alpha_shift, + (bytespp == 1)? 256 : 0, 0); /*GFX_MODE_FLAG_REVERSE_ALPHA);*/ + + return GFX_OK; +} + +static int +sdl_init(struct _gfx_driver *drv) +{ + int depth = 0; + int i; + + if (sdl_init_libsdl(drv)) + return GFX_FATAL; + + i = SDL_HWSURFACE | SDL_HWPALETTE; + if (flags & SCI_SDL_FULLSCREEN) { + i |= SDL_FULLSCREEN; + } + + depth = SDL_VideoModeOK(640,400, 32, i); + if (depth && (! sdl_init_specific(drv, 2, 2, depth >> 3 ))) + return GFX_OK; + + DEBUGB("Failed to find visual!\n"); + return GFX_FATAL; +} + +static void +sdl_exit(struct _gfx_driver *drv) +{ + int i; + if (S) { + for (i = 0; i < 2; i++) { + gfx_free_pixmap(drv, S->priority[i]); + S->priority[i] = NULL; + } + + for (i = 0; i < 3; i++) { + SDL_FreeSurface(S->visual[i]); + S->visual[i] = NULL; + } + + SDL_FreeCursor(SDL_GetCursor()); + + for (i = 0; i < 2; i++) + if (S->pointer_data[i]) { + free(S->pointer_data[i]); + S->pointer_data[i] = NULL; + } + + } + + SDL_QuitSubSystem(SDL_INIT_VIDEO); + + if (!SDL_WasInit(SDL_INIT_EVERYTHING)) { + SDLPRINTF("No active SDL subsystems found.. shutting down SDL\n"); + SDL_Quit(); + } +} + +static void +toggle_fullscreen(struct _gfx_driver *drv) +{ + rect_t src; + point_t dest; + + flags ^= SCI_SDL_FULLSCREEN; + if (sdl_alloc_primary(drv, XFACT, YFACT, drv->mode->bytespp)) { + SDLERROR("failed to switch to full-screen mode\n"); + /* Failed to set mode, revert to previous */ + flags ^= SCI_SDL_FULLSCREEN; + + if (sdl_alloc_primary(drv, XFACT, YFACT, drv->mode->bytespp)) { + /* This shouldn't happen... */ + SDLERROR("failed to revert to previous display mode\n"); + exit(-1); + } + } + + src.x = 0; + src.y = 0; + src.xl = XFACT * 320; + src.yl = YFACT * 200; + dest.x = 0; + dest.y = 0; + + drv->update(drv, src, dest, GFX_BUFFER_FRONT); +} + +/*** Drawing operations ***/ + +static Uint32 +sdl_map_color(gfx_driver_t *drv, gfx_color_t color) +{ + int opacity = 255 - color.alpha; + + if (drv->mode->palette && opacity < 255) { + if (opacity < 127) + opacity = 0; + else + opacity = 255; + + } + + if (drv->mode->palette) + return color.visual.global_index; + + return SDL_MapRGBA(S->visual[0]->format, + color.visual.r, + color.visual.g, + color.visual.b, + opacity); +} + +/* This code shamelessly lifted from the SDL_gfxPrimitives package */ +static void lineColor2(SDL_Surface *dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color) +{ + int pixx, pixy; + int x,y; + int dx,dy; + int sx,sy; + int swaptmp; + Uint8 *pixel; + + dx = x2 - x1; + dy = y2 - y1; + sx = (dx >= 0) ? 1 : -1; + sy = (dy >= 0) ? 1 : -1; + + dx = sx * dx + 1; + dy = sy * dy + 1; + pixx = dst->format->BytesPerPixel; + pixy = dst->pitch; + pixel = ((Uint8*)dst->pixels) + pixx * (int)x1 + pixy * (int)y1; + pixx *= sx; + pixy *= sy; + if (dx < dy) { + swaptmp = dx; dx = dy; dy = swaptmp; + swaptmp = pixx; pixx = pixy; pixy = swaptmp; + } + +/* Draw */ + x=0; + y=0; + switch(dst->format->BytesPerPixel) { + case 1: + for(; x < dx; x++, pixel += pixx) { + *pixel = color; + y += dy; + if (y >= dx) { + y -= dx; pixel += pixy; + } + } + break; + case 2: + for (; x < dx; x++, pixel += pixx) { + *(Uint16*)pixel = color; + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + case 3: + for(; x < dx; x++, pixel += pixx) { + if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { + pixel[0] = (color >> 16) & 0xff; + pixel[1] = (color >> 8) & 0xff; + pixel[2] = color & 0xff; + } else { + pixel[0] = color & 0xff; + pixel[1] = (color >> 8) & 0xff; + pixel[2] = (color >> 16) & 0xff; + } + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + case 4: + for(; x < dx; x++, pixel += pixx) { + *(Uint32*)pixel = color; + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + default: + fprintf(stderr, "invalid depth\n"); + } + +} + +static int +sdl_draw_line(struct _gfx_driver *drv, point_t start, point_t end, gfx_color_t color, + gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + Uint32 scolor; + int xfact = (line_mode == GFX_LINE_MODE_FINE)? 1: XFACT; + int yfact = (line_mode == GFX_LINE_MODE_FINE)? 1: YFACT; + int xsize = S->visual[1]->w; + int ysize = S->visual[1]->h; + + if (color.mask & GFX_MASK_VISUAL) { + int xc, yc; + point_t nstart, nend; + + scolor = sdl_map_color(drv, color); + + for (xc = 0; xc < xfact; xc++) + for (yc = 0; yc < yfact; yc++) { + nstart.x = start.x + xc; + nstart.y = start.y + yc; + nend.x = end.x + xc; + nend.y = end.y + yc; + + if (nstart.x < 0) + nstart.x = 0; + if (nend.x < 0) + nstart.x = 0; + if (nstart.y < 0) + nstart.y = 0; + if (nend.y < 0) + nend.y = 0; + if (nstart.x > xsize) + nstart.x = xsize; + if (nend.x >= xsize) + nend.x = xsize -1; + if (nstart.y > ysize) + nstart.y = ysize; + if (nend.y >= ysize) + nend.y = ysize -1; + +#if 0 + fprintf(stderr, "draw %d %d to %d %d %08x %d %d\n", nstart.x, + nstart.y, nend.x, nend.yl, scolor, xsize, ysize); +#endif + + SDL_LockSurface(S->visual[1]); + lineColor2(S->visual[1], (Sint16)nstart.x, (Sint16)nstart.y, + (Sint16)nend.x, (Sint16)nend.y, scolor); + SDL_UnlockSurface(S->visual[1]); + + if (color.mask & GFX_MASK_PRIORITY) { + gfx_draw_line_pixmap_i(S->priority[0], nstart, nend, + color.priority); + } + } + } + + return GFX_OK; +} + +static int +sdl_draw_filled_rect(struct _gfx_driver *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + Uint32 color; + SDL_Rect srect; + + if (color1.mask & GFX_MASK_VISUAL) { + color = sdl_map_color(drv, color1); + + srect.x = rect.x; + srect.y = rect.y; + srect.w = rect.xl; + srect.h = rect.yl; + + if (SDL_FillRect(S->visual[1], &srect, color)) + SDLERROR("Can't fill rect"); + } + + if (color1.mask & GFX_MASK_PRIORITY) + gfx_draw_box_pixmap_i(S->priority[0], rect, color1.priority); + + return GFX_OK; +} + +/*** Pixmap operations ***/ + +static int +sdl_register_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + SDL_Surface *reg_surface; + + if (pxm->internal.info) { + SDLERROR("Attempt to register pixmap twice!\n"); + return GFX_ERROR; + } + + reg_surface = + SDL_CreateRGBSurfaceFrom(pxm->data, pxm->xl, pxm->yl, + S->used_bytespp << 3, + S->used_bytespp * pxm->xl, + S->primary->format->Rmask, + S->primary->format->Gmask, + S->primary->format->Bmask, + S->alpha_mask); + + if (ALPHASURFACE) + SDL_SetAlpha(reg_surface, SDL_SRCALPHA,SDL_ALPHA_OPAQUE); + + pxm->internal.handle = SCI_SDL_HANDLE_NORMAL; + + DEBUGPXM("Registered surface %d/%d/%d at %p (%dx%d)\n", pxm->ID, pxm->loop, pxm->cel, + pxm->internal.info, pxm->xl, pxm->yl); + + pxm->internal.info = reg_surface; + + return GFX_OK; +} + +static int +sdl_unregister_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + DEBUGPXM("Freeing surface %d/%d/%d at %p\n", pxm->ID, pxm->loop, pxm->cel, + pxm->internal.info); + + if (!pxm->internal.info) { + SDLERROR("Attempt to unregister pixmap twice!\n"); + return GFX_ERROR; + } + + SDL_FreeSurface((SDL_Surface *) pxm->internal.info); + pxm->internal.info = NULL; + if (pxm->internal.handle != SCI_SDL_HANDLE_GRABBED) + free(pxm->data); + pxm->data = NULL; + return GFX_OK; +} + +static int +sdl_draw_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + int bufnr = (buffer == GFX_BUFFER_STATIC)? 2:1; + int pribufnr = bufnr -1; + + SDL_Surface *temp; + SDL_Rect srect; + SDL_Rect drect; + + if (dest.xl != src.xl || dest.yl != src.yl) { + SDLERROR("Attempt to scale pixmap (%dx%d)->(%dx%d): Not supported\n", + src.xl, src.yl, dest.xl, dest.yl); + return GFX_ERROR; + } + + srect.x = src.x; + srect.y = src.y; + srect.w = src.xl; + srect.h = src.yl; + drect.x = dest.x; + drect.y = dest.y; + drect.w = dest.xl; + drect.h = dest.yl; + + DEBUGU("Drawing %d (%d,%d)(%dx%d) onto (%d,%d)\n", pxm, srect.x, srect.y, + srect.w, srect.h, drect.x, drect.y); + + if (pxm->internal.handle == SCI_SDL_HANDLE_GRABBED) { + if (sdl_blit_surface(drv, (SDL_Surface *)pxm->internal.info, &srect , + S->visual[bufnr], &drect )) { + SDLERROR("blt failed"); + return GFX_ERROR; + } + return GFX_OK; + } + + temp = SDL_CreateRGBSurface(SDL_SWSURFACE, drect.w, drect.h, + S->used_bytespp << 3, + S->primary->format->Rmask, + S->primary->format->Gmask, + S->primary->format->Bmask, + S->alpha_mask); + + if (ALPHASURFACE) + SDL_SetAlpha(temp, SDL_SRCALPHA,SDL_ALPHA_OPAQUE); + + if (!temp) { + SDLERROR("Failed to allocate SDL surface"); + return GFX_ERROR; + } + + srect.x = dest.x; + srect.y = dest.y; + drect.x = 0; + drect.y = 0; + + if(sdl_blit_surface(drv, S->visual[bufnr], &srect, temp, &drect)) + SDLERROR("blt failed"); + + SDL_LockSurface(temp); + gfx_crossblit_pixmap(drv->mode, pxm, priority, src, dest, + (byte *) temp->pixels, temp->pitch, + S->priority[pribufnr]->index_data, + S->priority[pribufnr]->index_xl, 1, + GFX_CROSSBLIT_FLAG_DATA_IS_HOMED); + SDL_UnlockSurface(temp); + + srect.x = 0; + srect.y = 0; + drect.x = dest.x; + drect.y = dest.y; + + if(sdl_blit_surface(drv, temp, &srect, S->visual[bufnr], &drect)) + SDLERROR("blt failed"); + + SDL_FreeSurface(temp); + return GFX_OK; +} + +static int +sdl_grab_pixmap(struct _gfx_driver *drv, rect_t src, gfx_pixmap_t *pxm, + gfx_map_mask_t map) +{ + + + if (src.x < 0 || src.y < 0) { + SDLERROR("Attempt to grab pixmap from invalid coordinates (%d,%d)\n", src.x, src.y); + return GFX_ERROR; + } + + if (!pxm->data) { + SDLERROR("Attempt to grab pixmap to unallocated memory\n"); + return GFX_ERROR; + } + switch (map) { + + case GFX_MASK_VISUAL: { + SDL_Rect srect, drect; + SDL_Surface *temp; + + pxm->xl = src.xl; + pxm->yl = src.yl; + temp = SDL_CreateRGBSurface(SDL_SWSURFACE, src.xl, src.yl, + S->used_bytespp << 3, + S->primary->format->Rmask, + S->primary->format->Gmask, + S->primary->format->Bmask, + S->alpha_mask); + + if (!temp) { + SDLERROR("Failed to allocate SDL surface"); + return GFX_ERROR; + } + + if (SDL_MUSTLOCK(temp)) + sciprintf("Warning: SDL surface for pixmap grabbing requires locking\n"); + + if (ALPHASURFACE) + SDL_SetAlpha(temp, SDL_SRCALPHA,SDL_ALPHA_OPAQUE); + + srect.x = src.x; + srect.y = src.y; + srect.w = src.xl; + srect.h = src.yl; + drect.x = 0; + drect.y = 0; + drect.w = src.xl; + drect.h = src.yl; + + if (sdl_blit_surface(drv, S->visual[1], &srect, temp, &drect)) + SDLERROR("grab_pixmap: grab blit failed!\n"); + + pxm->internal.info = temp; + pxm->internal.handle = SCI_SDL_HANDLE_GRABBED; + pxm->flags |= GFX_PIXMAP_FLAG_INSTALLED | GFX_PIXMAP_FLAG_EXTERNAL_PALETTE | GFX_PIXMAP_FLAG_PALETTE_SET; + free(pxm->data); + pxm->data = (byte *) temp->pixels; + + DEBUGPXM("Grabbed surface %p (%dx%d)(%dx%d)\n", + pxm->internal.info, srect.x, srect.y, pxm->xl, pxm->yl); + + break; + } + + case GFX_MASK_PRIORITY: + SDLERROR("FIXME: priority map grab not implemented yet!\n"); + break; + + default: + SDLERROR("Attempt to grab pixmap from invalid map 0x%02x\n", map); + return GFX_ERROR; + } + + return GFX_OK; +} + + + /*** Buffer operations ***/ + +static int +sdl_update(struct _gfx_driver *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + int data_source = (buffer == GFX_BUFFER_BACK)? 2 : 1; + int data_dest = data_source - 1; + SDL_Rect srect, drect; + + if (src.x != dest.x || src.y != dest.y) { + DEBUGU("Updating %d (%d,%d)(%dx%d) to (%d,%d) on %d\n", buffer, src.x, src.y, + src.xl, src.yl, dest.x, dest.y, data_dest); + } else { + DEBUGU("Updating %d (%d,%d)(%dx%d) to %d\n", buffer, src.x, src.y, src.xl, src.yl, data_dest); + } + + srect.x = src.x; + srect.y = src.y; + srect.w = src.xl; + srect.h = src.yl; + drect.x = dest.x; + drect.y = dest.y; + drect.w = src.xl; + drect.h = src.yl; + + switch (buffer) { + case GFX_BUFFER_BACK: + if (sdl_blit_surface(drv, S->visual[data_source], &srect, + S->visual[data_dest], &drect)) + SDLERROR("surface update failed!\n"); + + if ((src.x == dest.x) && (src.y == dest.y)) + gfx_copy_pixmap_box_i(S->priority[0], S->priority[1], src); + break; + case GFX_BUFFER_FRONT: + if (sdl_blit_surface(drv, S->visual[data_source], &srect, S->primary, &drect)) + SDLERROR("primary surface update failed!\n"); + SDL_UpdateRect(S->primary, drect.x, drect.y, drect.w, drect.h); + break; + default: + GFXERROR("Invalid buffer %d in update!\n", buffer); + return GFX_ERROR; + } + + return GFX_OK; +} + +static int +sdl_set_static_buffer(struct _gfx_driver *drv, gfx_pixmap_t *pic, gfx_pixmap_t *priority) +{ + + if (!pic->internal.info) { + SDLERROR("Attempt to set static buffer with unregisterd pixmap!\n"); + return GFX_ERROR; + } + sdl_blit_surface(drv, (SDL_Surface *)pic->internal.info, NULL, + S->visual[2], NULL); + + gfx_copy_pixmap_box_i(S->priority[1], priority, gfx_rect(0, 0, 320*XFACT, 200*YFACT)); + + return GFX_OK; +} + + /*** Palette operations ***/ + +static int +sdl_set_palette(struct _gfx_driver *drv, int index, byte red, byte green, byte blue) +{ + if (index < 0 || index > 255) + { + SDLERROR("Attempt to set invalid palette entry %d\n", index); + return GFX_ERROR; + } + + S->colors[index].r = red; + S->colors[index].g = green; + S->colors[index].b = blue; + + SDL_SetColors(S->primary, S->colors + index, index, 1); + return GFX_OK; +} + + + /*** Mouse pointer operations ***/ + +byte * +sdl_create_cursor_rawdata(gfx_driver_t *drv, gfx_pixmap_t *pointer, int mode) +{ + int linewidth = (pointer->xl + 7) >> 3; + int lines = pointer->yl; + int xc, yc; + byte *data = (byte*)sci_calloc(linewidth, lines); + byte *linebase = data, *pos; + byte *src = pointer->index_data; + + for (yc = 0; yc < pointer->index_yl; yc++) { + int scalectr; + int bitc = 7; + pos = linebase; + + for (xc = 0; xc < pointer->index_xl; xc++) { + int draw = mode ? (*src == 0) : (*src < 255); + for (scalectr = 0; scalectr < XFACT; scalectr++) { + if (draw) + *pos |= (1 << bitc); + bitc--; + if (bitc < 0) { + bitc = 7; + pos++; + } + } + src++; + } + for (scalectr = 1; scalectr < YFACT; scalectr++) + memcpy(linebase + linewidth * scalectr, linebase, linewidth); + linebase += linewidth * YFACT; + } + return data; +} + + +static SDL_Cursor +*sdl_create_cursor_data(gfx_driver_t *drv, gfx_pixmap_t *pointer) +{ + byte *visual_data, *mask_data; + + S->pointer_data[0] = visual_data = sdl_create_cursor_rawdata(drv, pointer, 1); + S->pointer_data[1] = mask_data = sdl_create_cursor_rawdata(drv, pointer, 0); + + return SDL_CreateCursor(visual_data, mask_data, + pointer->xl, pointer->yl, + pointer->xoffset, pointer->yoffset); + +} + +static int sdl_set_pointer (struct _gfx_driver *drv, gfx_pixmap_t *pointer) +{ + int i; + + if (pointer == NULL) + SDL_ShowCursor(SDL_DISABLE); + else { + SDL_Cursor *cursor; + for (i = 0; i < 2; i++) + if (S->pointer_data[i]) { + free(S->pointer_data[i]); + S->pointer_data[i] = NULL; + } + + cursor = SDL_GetCursor(); + SDL_SetCursor(sdl_create_cursor_data(drv, pointer)); + SDL_FreeCursor(cursor); + SDL_ShowCursor(SDL_ENABLE); + } + + return 0; +} + +/*** Event management ***/ + +int +sdl_map_key(gfx_driver_t *drv, SDL_keysym keysym) +{ + SDLKey skey = keysym.sym; + int rkey = keysym.unicode & 0x7f; + + if ((skey >= SDLK_a) && (skey <= SDLK_z)) + return ('a' + (skey - SDLK_a)); + + if ((skey >= SDLK_0) && (skey <= SDLK_9)) + return ('0' + (skey - SDLK_0)); + + if (flags & SCI_SDL_SWAP_CTRL_CAPS) { + switch (skey) { + case SDLK_LCTRL: skey = SDLK_CAPSLOCK; break; + case SDLK_CAPSLOCK: skey = SDLK_LCTRL; break; + default: break; + } + } + + switch (skey) { + /* XXXX catch KMOD_NUM for KP0-9 */ + case SDLK_BACKSPACE: return SCI_K_BACKSPACE; + case SDLK_TAB: return 9; + case SDLK_ESCAPE: return SCI_K_ESC; + case SDLK_RETURN: + case SDLK_KP_ENTER: + if (SDL_GetModState() & KMOD_ALT) { + toggle_fullscreen(drv); + return 0; + } + return SCI_K_ENTER; + case SDLK_KP_PERIOD: return SCI_K_DELETE; + case SDLK_KP0: + case SDLK_INSERT: return SCI_K_INSERT; + case SDLK_KP1: + case SDLK_END: return SCI_K_END; + case SDLK_KP2: + case SDLK_DOWN: return SCI_K_DOWN; + case SDLK_KP3: + case SDLK_PAGEDOWN: return SCI_K_PGDOWN; + case SDLK_KP4: + case SDLK_LEFT: return SCI_K_LEFT; + case SDLK_KP5: return SCI_K_CENTER; + case SDLK_KP6: + case SDLK_RIGHT: return SCI_K_RIGHT; + case SDLK_KP7: + case SDLK_HOME: return SCI_K_HOME; + case SDLK_KP8: + case SDLK_UP: return SCI_K_UP; + case SDLK_KP9: + case SDLK_PAGEUP: return SCI_K_PGUP; + + case SDLK_F1: return SCI_K_F1; + case SDLK_F2: return SCI_K_F2; + case SDLK_F3: return SCI_K_F3; + case SDLK_F4: return SCI_K_F4; + case SDLK_F5: return SCI_K_F5; + case SDLK_F6: return SCI_K_F6; + case SDLK_F7: return SCI_K_F7; + case SDLK_F8: return SCI_K_F8; + case SDLK_F9: return SCI_K_F9; + case SDLK_F10: return SCI_K_F10; + + case SDLK_LCTRL: + case SDLK_RCTRL: + case SDLK_LALT: + case SDLK_RALT: + case SDLK_LMETA: + case SDLK_RMETA: + case SDLK_CAPSLOCK: + case SDLK_SCROLLOCK: + case SDLK_NUMLOCK: + case SDLK_LSHIFT: + case SDLK_RSHIFT: return 0; + + case SDLK_PLUS: + case SDLK_KP_PLUS: return '+'; + case SDLK_SLASH: + case SDLK_KP_DIVIDE: return '/'; + case SDLK_MINUS: + case SDLK_KP_MINUS: return '-'; + case SDLK_ASTERISK: + case SDLK_KP_MULTIPLY: return '*'; + case SDLK_EQUALS: + case SDLK_KP_EQUALS: return '='; + + case SDLK_COMMA: + case SDLK_PERIOD: + case SDLK_BACKSLASH: + case SDLK_SEMICOLON: + case SDLK_QUOTE: + case SDLK_LEFTBRACKET: + case SDLK_RIGHTBRACKET: + case SDLK_LESS: + case SDLK_DOLLAR: + case SDLK_GREATER: return rkey; + case SDLK_SPACE: return ' '; + +#ifdef MACOSX + case SDLK_WORLD_0: +#endif + case SDLK_BACKQUOTE: + if (keysym.mod & KMOD_CTRL) + return '`'; + else + return rkey; + + default: + break; + } + + sciprintf("Unknown SDL keysym: %04x (%d) \n", skey, rkey); + return 0; +} + + +void +sdl_fetch_event(gfx_driver_t *drv, sci_event_t *sci_event) +{ + SDL_Event event; + + while (SDL_PollEvent(&event)) { + + switch (event.type) { + case SDL_KEYDOWN: { + int modifiers = event.key.keysym.mod; + sci_event->type = SCI_EVT_KEYBOARD; + + S->buckystate = (((modifiers & KMOD_CAPS)? SCI_EVM_LSHIFT | SCI_EVM_RSHIFT : 0) + | ((modifiers & KMOD_CTRL)? SCI_EVM_CTRL : 0) + | ((modifiers & KMOD_ALT)? SCI_EVM_ALT : 0) + | ((modifiers & KMOD_NUM) ? SCI_EVM_NUMLOCK : 0) + | ((modifiers & KMOD_RSHIFT)? SCI_EVM_RSHIFT : 0) + | ((modifiers & KMOD_LSHIFT)? SCI_EVM_LSHIFT : 0)); + + sci_event->buckybits = S->buckystate; + sci_event->data = sdl_map_key(drv, event.key.keysym); + if (sci_event->data) + return; + break; + } + case SDL_MOUSEBUTTONDOWN: + sci_event->type = SCI_EVT_MOUSE_PRESS; + sci_event->buckybits = S->buckystate; + sci_event->data = event.button.button - 1; + drv->pointer_x = event.button.x; + drv->pointer_y = event.button.y; + return; + case SDL_MOUSEBUTTONUP: + sci_event->type = SCI_EVT_MOUSE_RELEASE; + sci_event->buckybits = S->buckystate; + sci_event->data = event.button.button - 1; + drv->pointer_x = event.button.x; + drv->pointer_y = event.button.y; + return; + case SDL_MOUSEMOTION: + drv->pointer_x = event.motion.x; + drv->pointer_y = event.motion.y; + break; + case SDL_QUIT: + sci_event->type = SCI_EVT_QUIT; + return; + break; + case SDL_VIDEOEXPOSE: + break; + default: + SDLERROR("Received unhandled SDL event %04x\n", event.type); + } + } + + sci_event->type = SCI_EVT_NONE; /* No event. */ +} + +static sci_event_t +sdl_get_event(struct _gfx_driver *drv) +{ + sci_event_t input; + + sdl_fetch_event(drv, &input); + return input; +} + +static int +sdl_usec_sleep(struct _gfx_driver *drv, long usecs) +{ + int msecs; + SDL_Event event; + + /* Wait at most 10ms to keep mouse cursor responsive. */ + msecs = usecs / 1000; + if (msecs > 10) + msecs = 10; + + SDL_PumpEvents(); + while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, + SDL_EVENTMASK(SDL_MOUSEMOTION)) == 1) { + drv->pointer_x = event.motion.x; + drv->pointer_y = event.motion.y; + } + + SDL_Delay(msecs); + + return GFX_OK; +} + +gfx_driver_t +gfx_driver_sdl = { + "sdl", + "0.3a", + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, + 0, 0, + GFX_CAPABILITY_MOUSE_SUPPORT | GFX_CAPABILITY_MOUSE_POINTER + | GFX_CAPABILITY_PIXMAP_REGISTRY | GFX_CAPABILITY_FINE_LINES, + 0, /*GFX_DEBUG_POINTER | GFX_DEBUG_UPDATES | GFX_DEBUG_PIXMAPS | GFX_DEBUG_BASIC, */ + sdl_set_parameter, + sdl_init_specific, + sdl_init, + sdl_exit, + sdl_draw_line, + sdl_draw_filled_rect, + sdl_register_pixmap, + sdl_unregister_pixmap, + sdl_draw_pixmap, + sdl_grab_pixmap, + sdl_update, + sdl_set_static_buffer, + sdl_set_pointer, + sdl_set_palette, + sdl_get_event, + sdl_usec_sleep, + NULL +}; + +#endif /* HAVE_SDL */ + + +/* reset to original optimisations for Win32: */ +/* (does not reset intrinsics) */ +#ifdef _WIN32 +//#pragma optimize( "", on ) +#endif diff --git a/engines/sci/gfx/drivers/xlib_driver.c b/engines/sci/gfx/drivers/xlib_driver.c new file mode 100644 index 0000000000..baa65d406b --- /dev/null +++ b/engines/sci/gfx/drivers/xlib_driver.c @@ -0,0 +1,1460 @@ +/*************************************************************************** + xlib_driver.h Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include +#ifndef X_DISPLAY_MISSING +#include + +#include +#include +#include +#include +#include + +#ifdef HAVE_MITSHM +#include +#include +#include +#if defined(HAVE_X11_EXTENSIONS_XRENDER_H) +# define HAVE_RENDER +# include +# if defined(HAVE_X11_XFT_XFT_H) +# include +# endif +#endif +#include +#endif + +#ifdef HAVE_XM_MWMUTIL_H +#include +#endif + +#define SCI_XLIB_PIXMAP_HANDLE_NORMAL 0 +#define SCI_XLIB_PIXMAP_HANDLE_GRABBED 1 + +#define SCI_XLIB_SWAP_CTRL_CAPS (1 << 0) +#define SCI_XLIB_INSERT_MODE (1 << 1) +#define SCI_XLIB_NLS (1 << 2) /* Non-US keyboard support, sacrificing shortcut keys */ + +#define X_COLOR_EXT(c) ((c << 8) | c) + +/* In C++ mode, we re-name ``class'' to ``class_''. However, this screws up +** our interface to xlib, so we're not doing it in here. +*/ +#ifdef class +# undef class +#endif + +static int flags; + +struct _xlib_state { + Display *display; + Window window; + GC gc; + XGCValues gc_values; + Colormap colormap; + Pixmap visual[3]; + gfx_pixmap_t *priority[2]; +#ifdef HAVE_MITSHM + XShmSegmentInfo *shm[4]; +#endif + int use_render; + int buckystate; + XErrorHandler old_error_handler; + Cursor mouse_cursor; + byte *pointer_data[2]; + int used_bytespp; /* bytes actually used to display stuff, rather than bytes occupied in data space */ + XVisualInfo visinfo; +#ifdef HAVE_RENDER + Picture picture; +#endif +}; + +#define VISUAL S->visinfo.visual + +#define S ((struct _xlib_state *)(drv->state)) + +#define XASS(foo) { int val = foo; if (!val) xlderror(drv, __LINE__); } +#define XFACT drv->mode->xfact +#define YFACT drv->mode->yfact + +#define DEBUGB if (drv->debug_flags & GFX_DEBUG_BASIC && ((debugline = __LINE__))) xldprintf +#define DEBUGU if (drv->debug_flags & GFX_DEBUG_UPDATES && ((debugline = __LINE__))) xldprintf +#define DEBUGPXM if (drv->debug_flags & GFX_DEBUG_PIXMAPS && ((debugline = __LINE__))) xldprintf +#define DEBUGPTR if (drv->debug_flags & GFX_DEBUG_POINTER && ((debugline = __LINE__))) xldprintf +#define ERROR if ((debugline = __LINE__)) xldprintf + +#ifdef HAVE_MITSHM + +XShmSegmentInfo shminfo; +int have_shmem = 0; +int x11_error = 0; + +static int check_for_xshm(Display *display) +{ + int major, minor, ignore; + Bool pixmaps; + if (XQueryExtension(display, "MIT-SHM", &ignore, &ignore, &ignore)) { + if (XShmQueryVersion( display, &major, &minor, &pixmaps) == True) { + return (pixmaps == True) ? 2 : 1 ; + } else { + return 0; + } + } + return 0; +} +#endif + +#ifdef HAVE_RENDER +static int x_have_render(Display *display) +{ + int ignore, retval; + + printf("Checking for X11 RENDER extension:"); + + retval = XQueryExtension(display, "RENDER", &ignore, &ignore, &ignore); + + if (retval) + printf(" found.\n"); + else + printf(" not found.\n"); + + return retval; +} +#endif + +static int debugline = 0; + +static void +xldprintf(const char *fmt, ...) +{ + va_list argp; + fprintf(stderr,"GFX-XLIB %d:", debugline); + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); +} + +static void +xlderror(gfx_driver_t *drv, int line) +{ + xldprintf("Xlib Error in line %d\n", line); +} + +static unsigned long +xlib_map_color(gfx_driver_t *drv, gfx_color_t color) +{ + gfx_mode_t *mode = drv->mode; + unsigned long temp; + unsigned long retval = 0; + + if (drv->mode->palette) + return color.visual.global_index; + + temp = color.visual.r; + temp |= temp << 8; + temp |= temp << 16; + retval |= (temp >> mode->red_shift) & (mode->red_mask); + temp = color.visual.g; + temp |= temp << 8; + temp |= temp << 16; + retval |= (temp >> mode->green_shift) & (mode->green_mask); + temp = color.visual.b; + temp |= temp << 8; + temp |= temp << 16; + retval |= (temp >> mode->blue_shift) & (mode->blue_mask); + + return retval; +} + + +static int +xlib_error_handler(Display *display, XErrorEvent *error) +{ + char errormsg[256]; +#ifdef HAVE_MITSHM + x11_error = 1; +#endif + XGetErrorText(display, error->error_code, errormsg, 255); + ERROR(" X11: %s\n", errormsg); + return 0; +} + +#define UPDATE_NLS_CAPABILITY \ + if (flags & SCI_XLIB_NLS) \ + drv->capabilities |= GFX_CAPABILITY_KEYTRANSLATE; \ + else \ + drv->capabilities &= ~GFX_CAPABILITY_KEYTRANSLATE + + + +static int +xlib_set_parameter(struct _gfx_driver *drv, char *attribute, char *value) +{ + if (!strncmp(attribute, "swap_ctrl_caps",17) || + !strncmp(attribute, "swap_caps_ctrl",17)) { + if (string_truep(value)) + flags |= SCI_XLIB_SWAP_CTRL_CAPS; + else + flags &= ~SCI_XLIB_SWAP_CTRL_CAPS; + + + return GFX_OK; + } + + if (!strncmp(attribute, "localised_keyboard", 18) + || !strncmp(attribute, "localized_keyboard", 18)) { + if (string_truep(value)) + flags |= SCI_XLIB_NLS; + else + flags &= ~SCI_XLIB_NLS; + + UPDATE_NLS_CAPABILITY; + + return GFX_OK; + + } + + if (!strncmp(attribute, "disable_shmem", 14)) { +#ifdef HAVE_MITSHM + if (string_truep(value)) + have_shmem = -1; +#endif + return GFX_OK; + } + + ERROR("Attempt to set xlib parameter \"%s\" to \"%s\"\n", attribute, value); + return GFX_ERROR; +} + +Cursor +x_empty_cursor(Display *display, Drawable drawable) /* Generates an empty X cursor */ +{ + byte cursor_data[] = {0}; + XColor black = {0,0,0}; + Pixmap cursor_map; + + Cursor retval; + + cursor_map = XCreateBitmapFromData(display, drawable, (char *) cursor_data, 1, 1); + + retval = XCreatePixmapCursor(display, cursor_map, cursor_map, &black, &black, 0, 0); + + XFreePixmap(display, cursor_map); + + return retval; +} + +static int +xlib_draw_filled_rect(struct _gfx_driver *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode); +static int +xlib_draw_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer); + + +static int +xlib_init_specific(struct _gfx_driver *drv, int xfact, int yfact, int bytespp) +{ + XVisualInfo xvisinfo; + XSetWindowAttributes win_attr; + int default_screen; + int vistype = (bytespp == 1)? 3 /* PseudoColor */ : 4 /* TrueColor */; + int red_shift, green_shift, blue_shift, alpha_shift; + int bytespp_physical; + int depth_mod; /* Number of bits to subtract from the depth while checking */ + unsigned int alpha_mask; + int xsize, ysize; + XSizeHints *size_hints; + XClassHint *class_hint; + XImage *foo_image = NULL; + int reverse_endian = 0; +#ifdef HAVE_XM_MWMUTIL_H + PropMotifWmHints motif_hints; + Atom prop, proptype; +#endif + + int i; + + + UPDATE_NLS_CAPABILITY; + + if (!drv->state /* = S */) + drv->state = sci_malloc(sizeof(struct _xlib_state)); + + flags |= SCI_XLIB_INSERT_MODE; + + S->display = XOpenDisplay(NULL); + + if (!S->display) { + ERROR("Could not open X connection!\n"); + return GFX_FATAL; + } + + default_screen = DefaultScreen(S->display); + + if (xfact == -1 && yfact == -1) { /* Detect (used INTERNALLY!) */ + xfact = 2; + if (DisplayWidth(S->display, default_screen) < 640 + || DisplayHeight(S->display, default_screen) < 400) + xfact = 1; + + yfact = xfact; + } + + xsize = xfact * 320; + ysize = yfact * 200; + if (xfact < 1 || yfact < 1 || bytespp < 1 || bytespp > 4) { + ERROR("Internal error: Attempt to open window w/ scale factors (%d,%d) and bpp=%d!\n", + xfact, yfact, bytespp); + BREAKPOINT(); + } + +#ifdef HAVE_MITSHM + if (!have_shmem) { + have_shmem = check_for_xshm(S->display); + if (have_shmem) { + printf("Using the MIT-SHM extension (%d/%d)\n", have_shmem, + XShmPixmapFormat(S->display)); + } + memset(&shminfo, 0, sizeof(XShmSegmentInfo)); + } + for (i = 0; i < 4; i++) + S->shm[i] = NULL; +#endif + + depth_mod = 0; + + /* Try all bit size twiddling */ + for (depth_mod = 0; depth_mod < 8; depth_mod++) + /* Try all viable depths */ + for (; bytespp > 0; bytespp--) + /* Try all interesting visuals */ + for (vistype = 4; vistype >= 3; vistype--) + if (XMatchVisualInfo(S->display, default_screen, + (bytespp << 3) - depth_mod, vistype, &xvisinfo)) + goto found_visual; + found_visual: + + S->visinfo = xvisinfo; + + if (vistype < 3 || ((vistype == 3) && (bytespp != 1))) { + if (bytespp == 1) { + ERROR("Could not get an 8 bit Pseudocolor visual!\n"); + } else { + ERROR("Could not get a %d bit TrueColor visual!\n", bytespp << 3); + } + return GFX_FATAL; + } + + S->colormap = win_attr.colormap = + XCreateColormap(S->display, RootWindow(S->display, default_screen), + VISUAL, (bytespp == 1)? AllocAll : AllocNone); + + win_attr.event_mask = PointerMotionMask | StructureNotifyMask | ButtonPressMask + | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | ExposureMask; + win_attr.background_pixel = win_attr.border_pixel = 0; + + S->window = XCreateWindow(S->display, RootWindow(S->display, default_screen), + 0, 0, xsize, ysize, 0, xvisinfo.depth, InputOutput, + VISUAL, (CWBackPixel | CWBorderPixel | CWColormap | CWEventMask), + &win_attr); + + if (!S->window) { + ERROR("Could not create window of size %dx%d!\n", 320*xfact, 200*yfact); + return GFX_FATAL; + } + + XSync(S->display, False); +#ifdef HAVE_XM_MWMUTIL_H + motif_hints.flags = MWM_HINTS_DECORATIONS|MWM_HINTS_FUNCTIONS; + motif_hints.decorations = MWM_DECOR_BORDER|MWM_DECOR_TITLE|MWM_DECOR_MENU|MWM_DECOR_MINIMIZE; + motif_hints.functions=0 +#ifdef MWM_FUNC_MOVE + | MWM_FUNC_MOVE +#endif +#ifdef MWM_FUNC_MINIMIZE + | MWM_FUNC_MINIMIZE +#endif +#ifdef MWM_FUNC_CLOSE + | MWM_FUNC_CLOSE +#endif +#ifdef MWM_FUNC_QUIT_APP + | MWM_FUNC_QUIT_APP +#endif + ; + + prop = XInternAtom(S->display, "_MOTIF_WM_HINTS", True ); + if (prop) { + proptype = prop; + XChangeProperty(S->display, S->window, prop, proptype, 32, PropModeReplace, (unsigned char *) &motif_hints, PROP_MOTIF_WM_HINTS_ELEMENTS); + } +#endif + + XStoreName(S->display, S->window, "FreeSCI"); + XDefineCursor(S->display, S->window, (S->mouse_cursor = x_empty_cursor(S->display, S->window))); + + XMapWindow(S->display, S->window); + S->buckystate = 0; + + if (bytespp == 1) + red_shift = green_shift = blue_shift = 0; + else { + red_shift = 32 - ffs((~xvisinfo.red_mask >> 1) & (xvisinfo.red_mask)); + green_shift = 32 - ffs((~xvisinfo.green_mask >> 1) & (xvisinfo.green_mask)); + blue_shift = 32 - ffs((~xvisinfo.blue_mask >> 1) & (xvisinfo.blue_mask)); + } + + class_hint = XAllocClassHint(); + class_hint->res_name = "FreeSCI"; + class_hint->res_class = "FreeSCI"; + XSetIconName(S->display, S->window, "FreeSCI"); + XSetClassHint(S->display, S->window, class_hint); + XFree(class_hint); + size_hints = XAllocSizeHints(); + size_hints->base_width = size_hints->min_width = size_hints->max_width = xsize; + size_hints->base_height = size_hints->min_height = size_hints->max_height = ysize; + size_hints->flags |= PMinSize | PMaxSize | PBaseSize; + XSetWMNormalHints(S->display, S->window, size_hints); + XFree(size_hints); + + S->gc_values.foreground = BlackPixel(S->display, DefaultScreen(S->display)); + S->gc = XCreateGC(S->display, S->window, GCForeground, &(S->gc_values)); + + for (i = 0; i < 2; i++) { + S->priority[i] = gfx_pixmap_alloc_index_data(gfx_new_pixmap(xsize, ysize, GFX_RESID_NONE, -i, -777)); + if (!S->priority[i]) { + ERROR("Out of memory: Could not allocate priority maps! (%dx%d)\n", + xsize, ysize); + return GFX_FATAL; + } + } + + foo_image = XCreateImage(S->display, VISUAL, + bytespp << 3, ZPixmap, 0, (char*)sci_malloc(23), 2, 2, 8, 0); + + bytespp_physical = foo_image->bits_per_pixel >> 3; +#ifdef WORDS_BIGENDIAN + reverse_endian = foo_image->byte_order == LSBFirst; +#else + reverse_endian = foo_image->byte_order == MSBFirst; +#endif + XDestroyImage(foo_image); + +#ifdef HAVE_MITSHM + /* set up and test the XSHM extension to make sure it's sane */ + if (have_shmem) { + XErrorHandler old_handler; + + x11_error = 0; + old_handler = XSetErrorHandler(xlib_error_handler); + + foo_image = XShmCreateImage(S->display, VISUAL, + bytespp_physical << 3, ZPixmap, 0, &shminfo, 2, 2); + if (foo_image) + shminfo.shmid = shmget(IPC_PRIVATE, + foo_image->bytes_per_line * + foo_image->height, + IPC_CREAT | 0777); + if (-1 == shminfo.shmid) { + have_shmem = 0; + ERROR("System does not support SysV IPC, disabling XSHM\n"); + perror("reason"); + foo_image = NULL; + } else { + + shminfo.shmaddr = (char *) shmat(shminfo.shmid, 0, 0); + if ((void *) -1 == shminfo.shmaddr) { + ERROR("Could not attach shared memory segment\n"); + perror("reason"); + if (foo_image) + XDestroyImage(foo_image); + return GFX_FATAL; + } + + foo_image->data = shminfo.shmaddr; + shminfo.readOnly = False; + + XShmAttach(S->display, &shminfo); + XSync(S->display, False); + shmctl(shminfo.shmid, IPC_RMID, 0); + + if (x11_error) { + have_shmem = 0; + ERROR("System does not support Shared XImages, disabling\n"); + shmdt(shminfo.shmaddr); + XDestroyImage(foo_image); + foo_image = NULL; + x11_error = 0; + } + XSetErrorHandler(old_handler); + } + } + +#endif + + + alpha_mask = xvisinfo.red_mask | xvisinfo.green_mask | xvisinfo.blue_mask; + if (!reverse_endian && bytespp_physical == 4 && (!(alpha_mask & 0xff000000) || !(alpha_mask & 0xff))) { + if (alpha_mask & 0xff) { + alpha_mask = 0xff000000; + alpha_shift = 0; + } else { /* Lowest byte */ + alpha_mask = 0x000000ff; + alpha_shift = 24; + } + } else { + alpha_mask = 0; + alpha_shift = 0; + } + /* create the visual buffers */ + for (i = 0; i < 3; i++) { +#ifdef HAVE_MITSHM + XErrorHandler old_handler; + + if (have_shmem && have_shmem != 2) { + ERROR("Shared memory pixmaps not supported. Reverting\n"); + perror("reason"); + have_shmem = 0; + } + + if (have_shmem) { + old_handler = XSetErrorHandler(xlib_error_handler); + + if ((S->shm[i] = (XShmSegmentInfo*)sci_malloc(sizeof(XShmSegmentInfo))) == 0) { + ERROR("AIEEEE! Malloc failed!\n"); + return GFX_FATAL; + } + memset(S->shm[i], 0, sizeof(XShmSegmentInfo)); + + S->shm[i]->shmid = shmget(IPC_PRIVATE, xsize * ysize * + bytespp_physical, + IPC_CREAT | IPC_EXCL | 0666); + S->shm[i]->readOnly = False; + + if (S->shm[i]->shmid == -1) { + have_shmem = 0; + ERROR("System does not support SysV IPC, disabling XSHM\n"); + perror("reason"); + } + } + if (have_shmem) { + S->shm[i]->shmaddr = (char *) shmat(S->shm[i]->shmid, 0, 0); + if (S->shm[i]->shmaddr == (void *) -1) { + ERROR("Could not attach shared memory segment\n"); + perror("reason"); + have_shmem = 0; + } + } + if (have_shmem) { + if (!XShmAttach(S->display, S->shm[i]) || x11_error) { + ERROR("ARGH! Can't attach shared memory segment\n"); + have_shmem = 0; + } + XSync(S->display, False); + shmctl(S->shm[i]->shmid, IPC_RMID, 0); + } + + if (have_shmem && !x11_error) { + S->visual[i] = XShmCreatePixmap(S->display, S->window, + S->shm[i]->shmaddr, + S->shm[i], xsize, ysize, + bytespp << 3); + XSync(S->display, False); + + if (x11_error || !S->visual[i]) { + ERROR("Shared Memory Pixmaps not supported on this system. Disabling!\n"); + have_shmem = 0; + XFreePixmap(S->display, S->visual[i]); + XShmDetach(S->display ,S->shm[i]); + XSync(S->display, False); + S->visual[i] = 0; + x11_error = 0; + shmdt(S->shm[i]->shmaddr); + sci_free(S->shm[i]); + + } + + } + XSetErrorHandler(old_handler); + + if (!have_shmem) +#endif + S->visual[i] = XCreatePixmap(S->display, S->window, xsize, ysize, bytespp << 3); + + XFillRectangle(S->display, S->visual[i], S->gc, 0, 0, xsize, ysize); + } + + /** X RENDER handling **/ +#ifdef HAVE_RENDER + S->use_render = x_have_render(S->display); + + if (S->use_render) { + XRenderPictFormat * format + = XRenderFindVisualFormat (S->display, + VISUAL); + S->picture = XRenderCreatePicture (S->display, + (Drawable) S->visual[1], + format, + 0, 0); + } else /* No Xrender */ + drv->draw_filled_rect = xlib_draw_filled_rect; +#else + S->use_render = 0; +#endif + /** End of X RENDER handling **/ + + + drv->mode = gfx_new_mode(xfact, yfact, bytespp_physical, + xvisinfo.red_mask, xvisinfo.green_mask, + xvisinfo.blue_mask, alpha_mask, + red_shift, green_shift, blue_shift, alpha_shift, + (bytespp == 1)? xvisinfo.colormap_size : 0, + (reverse_endian)? GFX_MODE_FLAG_REVERSE_ENDIAN : 0); + +#ifdef HAVE_MITSHM + if (have_shmem) { + XShmDetach(S->display, &shminfo); + XDestroyImage(foo_image); + shmdt(shminfo.shmaddr); + } +#endif + + S->used_bytespp = bytespp; + S->old_error_handler = (XErrorHandler) XSetErrorHandler(xlib_error_handler); + S->pointer_data[0] = NULL; + S->pointer_data[1] = NULL; + + + return GFX_OK; +} + + +static void +xlib_xdpy_info() +{ + int i; + XVisualInfo foo; + XVisualInfo *visuals; + int visuals_nr; + Display *display = XOpenDisplay(NULL); + const char *vis_classes[6] = {"StaticGray", "GrayScale", "StaticColor", + "PseudoColor", "TrueColor", "DirectColor"}; + + printf("Visuals provided by X11 server:\n"); + visuals = XGetVisualInfo(display, VisualNoMask, &foo, &visuals_nr); + + if (!visuals_nr) { + printf(" None!\n"); + } + + for (i = 0; i < visuals_nr; i++) { + XVisualInfo *visual = visuals + i; + + /* This works around an incompatibility between C++ and xlib: access visual->class */ + int visual_class = *((int *) (((byte *)(&(visual->depth))) + sizeof(unsigned int))); + + printf("%d:\t%d bpp %s(%d)\n" + "\tR:%08lx G:%08lx B:%08lx\n" + "\tbits_per_rgb=%d\n" + "\tcolormap_size=%d\n\n", + i, + visual->depth, + (visual_class < 0 || visual_class >5)? + "INVALID" : + vis_classes[visual_class], + visual_class, + visual->red_mask, visual->green_mask, visual->blue_mask, + visual->bits_per_rgb, visual->colormap_size); + + } + + if (visuals) + XFree(visuals); +} + +static int +xlib_init(struct _gfx_driver *drv) +{ + int i; + + /* Try 32-bit mode last due to compiz issue with bit depth 32. */ + for (i = 3; i > 0; i--) + if (!xlib_init_specific(drv, -1, -1, i)) + return GFX_OK; + + if (!xlib_init_specific(drv, -1, -1, 4)) + return GFX_OK; + + fprintf(stderr, "Could not find supported mode!\n"); + xlib_xdpy_info(); + + return GFX_FATAL; +} + +static void +xlib_exit(struct _gfx_driver *drv) +{ + int i; + if (S) { + for (i = 0; i < 2; i++) { + gfx_free_pixmap(drv, S->priority[i]); + S->priority[i] = NULL; + } + + for (i = 0; i < 3; i++) { +#ifdef HAVE_MITSHM + if (have_shmem && S->shm[i]) { + XFreePixmap(S->display, S->visual[i]); + XShmDetach(S->display, S->shm[i]); + + if (S->shm[i]->shmid >=0) + shmctl(S->shm[i]->shmid, IPC_RMID, 0); + if (S->shm[i]->shmaddr) + shmdt(S->shm[i]->shmaddr); + + sci_free(S->shm[i]); + S->shm[i] = NULL; + } else +#endif + XFreePixmap(S->display, S->visual[i]); + } + +#ifdef HAVE_RENDER + XRenderFreePicture(S->display, S->picture); +#endif + XFreeGC(S->display, S->gc); + XDestroyWindow(S->display, S->window); + XCloseDisplay(S->display); + XSetErrorHandler((XErrorHandler) (S->old_error_handler)); + sci_free(S); + drv->state /* = S */ = NULL; + gfx_free_mode(drv->mode); + } +} + + + /*** Drawing operations ***/ + +static int +xlib_draw_line(struct _gfx_driver *drv, point_t start, point_t end, gfx_color_t color, + gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + int linewidth = (line_mode == GFX_LINE_MODE_FINE)? 1: + (drv->mode->xfact + drv->mode->yfact) >> 1; + + if (color.mask & GFX_MASK_VISUAL) { + int xmod = drv->mode->xfact >> 1; + int ymod = drv->mode->yfact >> 1; + + if (line_mode == GFX_LINE_MODE_FINE) + xmod = ymod = 0; + + S->gc_values.foreground = xlib_map_color(drv, color); + S->gc_values.line_width = linewidth; + S->gc_values.line_style = (line_style == GFX_LINE_STYLE_NORMAL)? + LineSolid : LineOnOffDash; + S->gc_values.cap_style = CapProjecting; + + XChangeGC(S->display, S->gc, GCLineWidth | GCLineStyle | GCForeground | GCCapStyle, &(S->gc_values)); + + XASS(XDrawLine(S->display, S->visual[1], S->gc, + start.x + xmod, start.y + ymod, + end.x + xmod, end.y + ymod)); + } + + if (color.mask & GFX_MASK_PRIORITY) { + int xc, yc; + point_t nstart, nend; + + linewidth--; + for (xc = 0; xc <= linewidth; xc++) + for (yc = -linewidth; yc <= linewidth; yc++) { + nstart.x = start.x + xc; + nstart.y = start.y + yc; + + nend.x = end.x + xc; + nend.y = end.y + yc; + + gfx_draw_line_pixmap_i(S->priority[0], + nstart, nend, color.priority); + } + } + + return GFX_OK; +} + +static int +xlib_draw_filled_rect(struct _gfx_driver *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + if (color1.mask & GFX_MASK_VISUAL) { + S->gc_values.foreground = xlib_map_color(drv, color1); + XChangeGC(S->display, S->gc, GCForeground, &(S->gc_values)); + XASS(XFillRectangle(S->display, S->visual[1], S->gc, rect.x, rect.y, + rect.xl, rect.yl)); + } + + if (color1.mask & GFX_MASK_PRIORITY) + gfx_draw_box_pixmap_i(S->priority[0], rect, color1.priority); + + return GFX_OK; +} + +#ifdef HAVE_RENDER +static int +xlib_draw_filled_rect_RENDER(struct _gfx_driver *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + if (S->used_bytespp == 1) /* No room for alpha! */ + return xlib_draw_filled_rect(drv, rect, color1, color2, shade_mode); + + if (color1.mask & GFX_MASK_VISUAL) { + XRenderColor fg; + + fg.red = X_COLOR_EXT(color1.visual.r); + fg.green = X_COLOR_EXT(color1.visual.g); + fg.blue = X_COLOR_EXT(color1.visual.b); + fg.alpha = 0xffff - X_COLOR_EXT(color1.alpha); + + + XRenderFillRectangle(S->display, + PictOpOver, + S->picture, + &fg, + rect.x, rect.y, + rect.xl, rect.yl); + } + + if (color1.mask & GFX_MASK_PRIORITY) + gfx_draw_box_pixmap_i(S->priority[0], rect, color1.priority); + + return GFX_OK; +} +#endif + + /*** Pixmap operations ***/ + +static int +xlib_register_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + if (pxm->internal.info) { + ERROR("Attempt to register pixmap twice!\n"); + return GFX_ERROR; + } + pxm->internal.info = XCreateImage(S->display, VISUAL, + S->used_bytespp << 3, ZPixmap, 0, (char *) pxm->data, pxm->xl, + pxm->yl, 8, 0); + + DEBUGPXM("Registered pixmap %d/%d/%d at %p (%dx%d)\n", pxm->ID, pxm->loop, pxm->cel, + pxm->internal.info, pxm->xl, pxm->yl); + return GFX_OK; +} + +static int +xlib_unregister_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + DEBUGPXM("Freeing pixmap %d/%d/%d at %p\n", pxm->ID, pxm->loop, pxm->cel, + pxm->internal.info); + + if (!pxm->internal.info) { + ERROR("Attempt to unregister pixmap twice!\n"); + return GFX_ERROR; + } + + XDestroyImage((XImage *) pxm->internal.info); + pxm->internal.info = NULL; + pxm->data = NULL; /* Freed by XDestroyImage */ + return GFX_OK; +} + +static int +xlib_draw_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + int bufnr = (buffer == GFX_BUFFER_STATIC)? 2:1; + int pribufnr = bufnr -1; + XImage *tempimg; + + if (dest.xl != src.xl || dest.yl != src.yl) { + ERROR("Attempt to scale pixmap (%dx%d)->(%dx%d): Not supported\n", + src.xl, src.yl, dest.xl, dest.yl); + return GFX_ERROR; + } + + if (pxm->internal.handle == SCI_XLIB_PIXMAP_HANDLE_GRABBED) { + XPutImage(S->display, S->visual[bufnr], S->gc, (XImage *) pxm->internal.info, + src.x, src.y, dest.x, dest.y, dest.xl, dest.yl); + return GFX_OK; + } + + tempimg = XGetImage(S->display, S->visual[bufnr], dest.x, dest.y, + dest.xl, dest.yl, 0xffffffff, ZPixmap); + + if (!tempimg) { + ERROR("Failed to grab X image!\n"); + return GFX_ERROR; + } + + gfx_crossblit_pixmap(drv->mode, pxm, priority, src, dest, + (byte *) tempimg->data, tempimg->bytes_per_line, + S->priority[pribufnr]->index_data, + S->priority[pribufnr]->index_xl, 1, + GFX_CROSSBLIT_FLAG_DATA_IS_HOMED); + + XPutImage(S->display, S->visual[bufnr], S->gc, tempimg, + 0, 0, dest.x, dest.y, dest.xl, dest.yl); + + XDestroyImage(tempimg); + return GFX_OK; +} + + +static int +xlib_grab_pixmap(struct _gfx_driver *drv, rect_t src, gfx_pixmap_t *pxm, + gfx_map_mask_t map) +{ + + if (src.x < 0 || src.y < 0) { + ERROR("Attempt to grab pixmap from invalid coordinates (%d,%d)\n", src.x, src.y); + return GFX_ERROR; + } + + if (!pxm->data) { + ERROR("Attempt to grab pixmap to unallocated memory\n"); + return GFX_ERROR; + } + + switch (map) { + + case GFX_MASK_VISUAL: + pxm->xl = src.xl; + pxm->yl = src.yl; + + pxm->internal.info = XGetImage(S->display, S->visual[1], src.x, src.y, + src.xl, src.yl, 0xffffffff, ZPixmap); + pxm->internal.handle = SCI_XLIB_PIXMAP_HANDLE_GRABBED; + pxm->flags |= GFX_PIXMAP_FLAG_INSTALLED | GFX_PIXMAP_FLAG_EXTERNAL_PALETTE | GFX_PIXMAP_FLAG_PALETTE_SET; + sci_free(pxm->data); + pxm->data = (byte *) ((XImage *)(pxm->internal.info))->data; + break; + + case GFX_MASK_PRIORITY: + ERROR("FIXME: priority map grab not implemented yet!\n"); + break; + + default: + ERROR("Attempt to grab pixmap from invalid map 0x%02x\n", map); + return GFX_ERROR; + } + + return GFX_OK; +} + + + /*** Buffer operations ***/ + +static int +xlib_update(struct _gfx_driver *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + int data_source = (buffer == GFX_BUFFER_BACK)? 2 : 1; + int data_dest = data_source - 1; + + + if (src.x != dest.x || src.y != dest.y) { + DEBUGU("Updating %d (%d,%d)(%dx%d) to (%d,%d)\n", buffer, src.x, src.y, + src.xl, src.yl, dest.x, dest.y); + } else { + DEBUGU("Updating %d (%d,%d)(%dx%d)\n", buffer, src.x, src.y, src.xl, src.yl); + } + + XCopyArea(S->display, S->visual[data_source], S->visual[data_dest], S->gc, + src.x, src.y, src.xl, src.yl, dest.x, dest.y); + + if (buffer == GFX_BUFFER_BACK && (src.x == dest.x) && (src.y == dest.y)) + gfx_copy_pixmap_box_i(S->priority[0], S->priority[1], src); + else { + gfx_color_t col; + col.mask = GFX_MASK_VISUAL; + col.visual.r = 0xff; + col.visual.g = 0; + col.visual.b = 0; + + XCopyArea(S->display, S->visual[0], S->window, S->gc, + dest.x, dest.y, src.xl, src.yl, dest.x, dest.y); + } + + return GFX_OK; +} + +static int +xlib_set_static_buffer(struct _gfx_driver *drv, gfx_pixmap_t *pic, gfx_pixmap_t *priority) +{ + + if (!pic->internal.info) { + ERROR("Attempt to set static buffer with unregisterd pixmap!\n"); + return GFX_ERROR; + } + XPutImage(S->display, S->visual[2], S->gc, (XImage *) pic->internal.info, + 0, 0, 0, 0, 320 * XFACT, 200 * YFACT); + gfx_copy_pixmap_box_i(S->priority[1], priority, gfx_rect(0, 0, 320*XFACT, 200*YFACT)); + + return GFX_OK; +} + + + /*** Mouse pointer operations ***/ + +byte * +xlib_create_cursor_data(gfx_driver_t *drv, gfx_pixmap_t *pointer, int mode) +{ + int linewidth = (pointer->xl + 7) >> 3; + int lines = pointer->yl; + int xc, yc; + int xfact = drv->mode->xfact; + byte *data = (byte*)sci_calloc(linewidth, lines); + byte *linebase = data, *pos; + byte *src = pointer->index_data; + + + for (yc = 0; yc < pointer->index_yl; yc++) { + int scalectr; + int bitc = 0; + pos = linebase; + + + for (xc = 0; xc < pointer->index_xl; xc++) { + int draw = mode? + (*src == 0) : (*src < 255); + + for (scalectr = 0; scalectr < xfact; scalectr++) { + if (draw) + *pos |= (1 << bitc); + + bitc++; + if (bitc == 8) { + bitc = 0; + pos++; + } + } + + src++; + } + for (scalectr = 1; scalectr < drv->mode->yfact; scalectr++) + memcpy(linebase + linewidth * scalectr, linebase, linewidth); + + linebase += linewidth * drv->mode->yfact; + } + + return data; +} + +static int +xlib_set_pointer(struct _gfx_driver *drv, gfx_pixmap_t *pointer) +{ + int i; + XFreeCursor(S->display, S->mouse_cursor); + + for (i = 0; i < 2; i++) + if (S->pointer_data[i]) { + sci_free(S->pointer_data[i]); + S->pointer_data[i] = NULL; + } + + if (pointer == NULL) + S->mouse_cursor = x_empty_cursor(S->display, S->window); + else { + XColor cols[2]; + Pixmap visual, mask; + byte *mask_data, *visual_data; + int real_xl = ((pointer->xl + 7) >> 3) << 3; + int i; + + for (i = 0; i < 2; i++) { + cols[i].red = pointer->colors[i].r; + cols[i].red |= (cols[i].red << 8); + cols[i].green = pointer->colors[i].g; + cols[i].green |= (cols[i].green << 8); + cols[i].blue = pointer->colors[i].b; + cols[i].blue |= (cols[i].blue << 8); + } + + S->pointer_data[0] = visual_data = xlib_create_cursor_data(drv, pointer, 1); + S->pointer_data[1] = mask_data = xlib_create_cursor_data(drv, pointer, 0); + S->pointer_data[0] = NULL; + S->pointer_data[1] = NULL; + visual = XCreateBitmapFromData(S->display, S->window, (char *) visual_data, real_xl, pointer->yl); + mask = XCreateBitmapFromData(S->display, S->window, (char *) mask_data, real_xl, pointer->yl); + + + S->mouse_cursor = + XCreatePixmapCursor(S->display, visual, mask, + &(cols[0]), &(cols[1]), + pointer->xoffset, pointer->yoffset); + + XFreePixmap(S->display, visual); + XFreePixmap(S->display, mask); + sci_free(mask_data); + sci_free(visual_data); + } + + XDefineCursor(S->display, S->window, S->mouse_cursor); + + return 0; +} + + + /*** Palette operations ***/ + +static int +xlib_set_palette(struct _gfx_driver *drv, int index, byte red, byte green, byte blue) +{ + char stringbuf[8]; + sprintf(stringbuf, "#%02x%02x%02x", red, green, blue); /* Argh. */ + + XStoreNamedColor(S->display, S->colormap, stringbuf, index, DoRed | DoGreen | DoBlue); + /* Isn't there some way to do this without strings? */ + + return GFX_OK; +} + + + /*** Event management ***/ +/* +int +x_unmap_key(gfx_driver_t *drv, int keycode) +{ + KeySym xkey = XKeycodeToKeysym(S->display, keycode, 0); + + return 0; +} +*/ +int +x_map_key(gfx_driver_t *drv, XEvent *key_event, char *character) +{ + KeySym xkey = XKeycodeToKeysym(S->display, key_event->xkey.keycode, 0); + + *character = 0; + + if (flags & SCI_XLIB_SWAP_CTRL_CAPS) { + switch (xkey) { + case XK_Control_L: xkey = XK_Caps_Lock; break; + case XK_Caps_Lock: xkey = XK_Control_L; break; + } + } + + switch(xkey) { + + case XK_BackSpace: return SCI_K_BACKSPACE; + case XK_Tab: return 9; + case XK_Escape: return SCI_K_ESC; + case XK_Return: + case XK_KP_Enter: return SCI_K_ENTER; + + case XK_KP_Decimal: + case XK_KP_Delete: return SCI_K_DELETE; + case XK_KP_0: + case XK_KP_Insert: return SCI_K_INSERT; + case XK_End: + case XK_KP_End: + case XK_KP_1: return SCI_K_END; + case XK_Down: + case XK_KP_Down: + case XK_KP_2: return SCI_K_DOWN; + case XK_Page_Down: + case XK_KP_Page_Down: + case XK_KP_3: return SCI_K_PGDOWN; + case XK_Left: + case XK_KP_Left: + case XK_KP_4: return SCI_K_LEFT; + case XK_KP_Begin: + case XK_KP_5: return SCI_K_CENTER; + case XK_Right: + case XK_KP_Right: + case XK_KP_6: return SCI_K_RIGHT; + case XK_Home: + case XK_KP_Home: + case XK_KP_7: return SCI_K_HOME; + case XK_Up: + case XK_KP_Up: + case XK_KP_8: return SCI_K_UP; + case XK_Page_Up: + case XK_KP_Page_Up: + case XK_KP_9: return SCI_K_PGUP; + + case XK_F1: return SCI_K_F1; + case XK_F2: return SCI_K_F2; + case XK_F3: return SCI_K_F3; + case XK_F4: return SCI_K_F4; + case XK_F5: return SCI_K_F5; + case XK_F6: return SCI_K_F6; + case XK_F7: return SCI_K_F7; + case XK_F8: return SCI_K_F8; + case XK_F9: return SCI_K_F9; + case XK_F10: return SCI_K_F10; + + + case XK_Control_L: + case XK_Control_R:/* S->buckystate |= SCI_EVM_CTRL; return 0; */ + case XK_Alt_L: + case XK_Alt_R:/* S->buckystate |= SCI_EVM_ALT; return 0; */ + case XK_Caps_Lock: + case XK_Shift_Lock:/* S->buckystate ^= SCI_EVM_CAPSLOCK; return 0; */ + case XK_Scroll_Lock:/* S->buckystate ^= SCI_EVM_SCRLOCK; return 0; */ + case XK_Num_Lock:/* S->buckystate ^= SCI_EVM_NUMLOCK; return 0; */ + case XK_Shift_L:/* S->buckystate |= SCI_EVM_LSHIFT; return 0; */ + case XK_Shift_R:/* S->buckystate |= SCI_EVM_RSHIFT; return 0; */ + return 0; + default: + break; + } + + + if (flags & SCI_XLIB_NLS) { + /* Localised key lookup */ + XLookupString(&(key_event->xkey), character, 1, &xkey, NULL); + } + + if ((xkey >= ' ') && (xkey <= '~')) + return xkey; /* All printable ASCII characters */ + + switch (xkey) { + case XK_KP_Add: return '+'; + case XK_KP_Divide: return '/'; + case XK_KP_Subtract: return '-'; + case XK_KP_Multiply: return '*'; + } + + if (*character) + return xkey; /* Should suffice for all practical purposes */ + + sciprintf("Unknown X keysym: %04x\n", xkey); + return 0; +} + + +void +x_get_event(gfx_driver_t *drv, int eventmask, long wait_usec, sci_event_t *sci_event) +{ + int x_button_xlate[] = {0, 1, 3, 2, 4, 5}; + XEvent event; + Window window = S->window; + Display *display = S->display; + struct timeval ctime, timeout_time, sleep_time; + long usecs_to_sleep; + + eventmask |= ExposureMask; /* Always check for this */ + + gettimeofday(&timeout_time, NULL); + timeout_time.tv_usec += wait_usec; + + /* Calculate wait time */ + timeout_time.tv_sec += (timeout_time.tv_usec / 1000000); + timeout_time.tv_usec %= 1000000; + + do { + int hasnext_event = 1; + + while (hasnext_event) { + if (sci_event) { /* Capable of handling any event? */ + hasnext_event = XPending(display); + if (hasnext_event) + XNextEvent(display, &event); + } else + hasnext_event = XCheckWindowEvent(display, window, eventmask, &event); + + + if (hasnext_event) + switch (event.type) { + + case ReparentNotify: + case ConfigureNotify: + case MapNotify: + case UnmapNotify: + break; + + case KeyPress: { + int modifiers = event.xkey.state; + char ch = 0; + sci_event->type = SCI_EVT_KEYBOARD; + + S->buckystate = ((flags & SCI_XLIB_INSERT_MODE)? SCI_EVM_INSERT : 0) + | (((modifiers & LockMask)? SCI_EVM_LSHIFT | SCI_EVM_RSHIFT : 0) + | ((modifiers & ControlMask)? SCI_EVM_CTRL : 0) + | ((modifiers & (Mod1Mask | Mod4Mask))? SCI_EVM_ALT : 0) + | ((modifiers & Mod2Mask)? SCI_EVM_NUMLOCK : 0) + | ((modifiers & Mod5Mask)? SCI_EVM_SCRLOCK : 0)) + ^ ((modifiers & ShiftMask)? SCI_EVM_LSHIFT | SCI_EVM_RSHIFT : 0); + + sci_event->buckybits = S->buckystate; + sci_event->data = + x_map_key(drv, &event, &ch); + + if (ch) + sci_event->character = ch; + else + sci_event->character = sci_event->data; + + if (sci_event->data == SCI_K_INSERT) + flags ^= SCI_XLIB_INSERT_MODE; + + if (sci_event->data) + return; + + break; + } + + case KeyRelease: + /*x_unmap_key(drv, event.xkey.keycode);*/ + break; + + case ButtonPress: { + sci_event->type = SCI_EVT_MOUSE_PRESS; + sci_event->buckybits = S->buckystate; + sci_event->data = x_button_xlate[event.xbutton.button]; + return; + } + + case ButtonRelease: { + sci_event->type = SCI_EVT_MOUSE_RELEASE; + sci_event->buckybits = S->buckystate; + sci_event->data = x_button_xlate[event.xbutton.button]; + return; + } + + case MotionNotify: { + + drv->pointer_x = event.xmotion.x; + drv->pointer_y = event.xmotion.y; + if (!sci_event) + /* Wake up from sleep */ + return; + } + break; + + case GraphicsExpose: + case Expose: { + XCopyArea(S->display, S->visual[0], S->window, S->gc, + event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height, + event.xexpose.x, event.xexpose.y); + } + break; + + case NoExpose: + break; + + default: + ERROR("Received unhandled X event %04x\n", event.type); + } + } + + gettimeofday(&ctime, NULL); + + usecs_to_sleep = (timeout_time.tv_sec > ctime.tv_sec)? 1000000 : 0; + usecs_to_sleep += timeout_time.tv_usec - ctime.tv_usec; + if (ctime.tv_sec > timeout_time.tv_sec) usecs_to_sleep = -1; + + + if (usecs_to_sleep > 0) { + + if (usecs_to_sleep > 10000) + usecs_to_sleep = 10000; /* Sleep for a maximum of 10 ms */ + + sleep_time.tv_usec = usecs_to_sleep; + sleep_time.tv_sec = 0; + + select(0, NULL, NULL, NULL, &sleep_time); /* Sleep. */ + } + + } while (usecs_to_sleep >= 0); + + if (sci_event) + sci_event->type = SCI_EVT_NONE; /* No event. */ +} + + +static sci_event_t +xlib_get_event(struct _gfx_driver *drv) +{ + sci_event_t input; + + x_get_event(drv, PointerMotionMask | StructureNotifyMask | ButtonPressMask + | ButtonReleaseMask | KeyPressMask | KeyReleaseMask, + 0, &input); + + return input; +} + + +static int +xlib_usec_sleep(struct _gfx_driver *drv, long usecs) +{ + x_get_event(drv, PointerMotionMask | StructureNotifyMask, usecs, NULL); + return GFX_OK; +} + +gfx_driver_t +gfx_driver_xlib = { + "xlib", + "0.6a", + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, + 0, 0, + GFX_CAPABILITY_STIPPLED_LINES | GFX_CAPABILITY_MOUSE_SUPPORT + | GFX_CAPABILITY_MOUSE_POINTER | GFX_CAPABILITY_PIXMAP_REGISTRY + | GFX_CAPABILITY_FINE_LINES | GFX_CAPABILITY_WINDOWED + | GFX_CAPABILITY_KEYTRANSLATE, + 0/*GFX_DEBUG_POINTER | GFX_DEBUG_UPDATES | GFX_DEBUG_PIXMAPS | GFX_DEBUG_BASIC*/, + xlib_set_parameter, + xlib_init_specific, + xlib_init, + xlib_exit, + xlib_draw_line, +#ifdef HAVE_RENDER + xlib_draw_filled_rect_RENDER, +#else + xlib_draw_filled_rect, +#endif + xlib_register_pixmap, + xlib_unregister_pixmap, + xlib_draw_pixmap, + xlib_grab_pixmap, + xlib_update, + xlib_set_static_buffer, + xlib_set_pointer, + xlib_set_palette, + xlib_get_event, + xlib_usec_sleep, + NULL +}; + +#endif /* X_DISPLAY_MISSING */ diff --git a/engines/sci/gfx/font-5x8.c b/engines/sci/gfx/font-5x8.c new file mode 100644 index 0000000000..9fe2e587e7 --- /dev/null +++ b/engines/sci/gfx/font-5x8.c @@ -0,0 +1,2580 @@ +/* Auto-generated by bdftofont.c */ + +#include + +static int gfxfont_5x8_widths[] = { + 5, /* 0x00 */ + 5, /* 0x01 */ + 5, /* 0x02 */ + 5, /* 0x03 */ + 5, /* 0x04 */ + 5, /* 0x05 */ + 5, /* 0x06 */ + 5, /* 0x07 */ + 5, /* 0x08 */ + 5, /* 0x09 */ + 5, /* 0x0a */ + 5, /* 0x0b */ + 5, /* 0x0c */ + 5, /* 0x0d */ + 5, /* 0x0e */ + 5, /* 0x0f */ + 5, /* 0x10 */ + 5, /* 0x11 */ + 5, /* 0x12 */ + 5, /* 0x13 */ + 5, /* 0x14 */ + 5, /* 0x15 */ + 5, /* 0x16 */ + 5, /* 0x17 */ + 5, /* 0x18 */ + 5, /* 0x19 */ + 5, /* 0x1a */ + 5, /* 0x1b */ + 5, /* 0x1c */ + 5, /* 0x1d */ + 5, /* 0x1e */ + 5, /* 0x1f */ + 5, /* 0x20 */ + 2, /* 0x21 */ + 4, /* 0x22 */ + 6, /* 0x23 */ + 6, /* 0x24 */ + 6, /* 0x25 */ + 6, /* 0x26 */ + 2, /* 0x27 */ + 3, /* 0x28 */ + 3, /* 0x29 */ + 5, /* 0x2a */ + 6, /* 0x2b */ + 3, /* 0x2c */ + 5, /* 0x2d */ + 2, /* 0x2e */ + 7, /* 0x2f */ + 5, /* 0x30 */ + 4, /* 0x31 */ + 5, /* 0x32 */ + 5, /* 0x33 */ + 5, /* 0x34 */ + 5, /* 0x35 */ + 5, /* 0x36 */ + 5, /* 0x37 */ + 5, /* 0x38 */ + 5, /* 0x39 */ + 2, /* 0x3a */ + 3, /* 0x3b */ + 4, /* 0x3c */ + 5, /* 0x3d */ + 4, /* 0x3e */ + 5, /* 0x3f */ + 6, /* 0x40 */ + 5, /* 0x41 */ + 5, /* 0x42 */ + 5, /* 0x43 */ + 5, /* 0x44 */ + 5, /* 0x45 */ + 5, /* 0x46 */ + 5, /* 0x47 */ + 5, /* 0x48 */ + 4, /* 0x49 */ + 5, /* 0x4a */ + 5, /* 0x4b */ + 5, /* 0x4c */ + 6, /* 0x4d */ + 5, /* 0x4e */ + 5, /* 0x4f */ + 5, /* 0x50 */ + 5, /* 0x51 */ + 5, /* 0x52 */ + 5, /* 0x53 */ + 4, /* 0x54 */ + 5, /* 0x55 */ + 5, /* 0x56 */ + 6, /* 0x57 */ + 5, /* 0x58 */ + 6, /* 0x59 */ + 5, /* 0x5a */ + 4, /* 0x5b */ + 7, /* 0x5c */ + 4, /* 0x5d */ + 4, /* 0x5e */ + 4, /* 0x5f */ + 3, /* 0x60 */ + 5, /* 0x61 */ + 5, /* 0x62 */ + 4, /* 0x63 */ + 5, /* 0x64 */ + 5, /* 0x65 */ + 5, /* 0x66 */ + 5, /* 0x67 */ + 5, /* 0x68 */ + 4, /* 0x69 */ + 4, /* 0x6a */ + 5, /* 0x6b */ + 4, /* 0x6c */ + 6, /* 0x6d */ + 5, /* 0x6e */ + 5, /* 0x6f */ + 5, /* 0x70 */ + 5, /* 0x71 */ + 5, /* 0x72 */ + 4, /* 0x73 */ + 5, /* 0x74 */ + 5, /* 0x75 */ + 4, /* 0x76 */ + 6, /* 0x77 */ + 5, /* 0x78 */ + 5, /* 0x79 */ + 5, /* 0x7a */ + 5, /* 0x7b */ + 2, /* 0x7c */ + 5, /* 0x7d */ + 5, /* 0x7e */ + 5, /* 0x7f */ + 5, /* 0x80 */ + 5, /* 0x81 */ + 5, /* 0x82 */ + 5, /* 0x83 */ + 5, /* 0x84 */ + 5, /* 0x85 */ + 5, /* 0x86 */ + 5, /* 0x87 */ + 5, /* 0x88 */ + 5, /* 0x89 */ + 5, /* 0x8a */ + 5, /* 0x8b */ + 5, /* 0x8c */ + 5, /* 0x8d */ + 5, /* 0x8e */ + 5, /* 0x8f */ + 5, /* 0x90 */ + 5, /* 0x91 */ + 5, /* 0x92 */ + 5, /* 0x93 */ + 5, /* 0x94 */ + 5, /* 0x95 */ + 5, /* 0x96 */ + 5, /* 0x97 */ + 5, /* 0x98 */ + 5, /* 0x99 */ + 5, /* 0x9a */ + 5, /* 0x9b */ + 5, /* 0x9c */ + 5, /* 0x9d */ + 5, /* 0x9e */ + 5, /* 0x9f */ + 5, /* 0xa0 */ + 5, /* 0xa1 */ + 5, /* 0xa2 */ + 5, /* 0xa3 */ + 5, /* 0xa4 */ + 5, /* 0xa5 */ + 5, /* 0xa6 */ + 5, /* 0xa7 */ + 5, /* 0xa8 */ + 5, /* 0xa9 */ + 5, /* 0xaa */ + 5, /* 0xab */ + 5, /* 0xac */ + 5, /* 0xad */ + 5, /* 0xae */ + 5, /* 0xaf */ + 5, /* 0xb0 */ + 5, /* 0xb1 */ + 5, /* 0xb2 */ + 5, /* 0xb3 */ + 5, /* 0xb4 */ + 5, /* 0xb5 */ + 5, /* 0xb6 */ + 5, /* 0xb7 */ + 5, /* 0xb8 */ + 5, /* 0xb9 */ + 5, /* 0xba */ + 5, /* 0xbb */ + 5, /* 0xbc */ + 5, /* 0xbd */ + 5, /* 0xbe */ + 5, /* 0xbf */ + 5, /* 0xc0 */ + 5, /* 0xc1 */ + 5, /* 0xc2 */ + 5, /* 0xc3 */ + 5, /* 0xc4 */ + 5, /* 0xc5 */ + 5, /* 0xc6 */ + 5, /* 0xc7 */ + 5, /* 0xc8 */ + 5, /* 0xc9 */ + 5, /* 0xca */ + 5, /* 0xcb */ + 5, /* 0xcc */ + 5, /* 0xcd */ + 5, /* 0xce */ + 5, /* 0xcf */ + 5, /* 0xd0 */ + 5, /* 0xd1 */ + 5, /* 0xd2 */ + 5, /* 0xd3 */ + 5, /* 0xd4 */ + 5, /* 0xd5 */ + 5, /* 0xd6 */ + 5, /* 0xd7 */ + 5, /* 0xd8 */ + 5, /* 0xd9 */ + 5, /* 0xda */ + 5, /* 0xdb */ + 5, /* 0xdc */ + 5, /* 0xdd */ + 5, /* 0xde */ + 5, /* 0xdf */ + 5, /* 0xe0 */ + 5, /* 0xe1 */ + 5, /* 0xe2 */ + 5, /* 0xe3 */ + 5, /* 0xe4 */ + 5, /* 0xe5 */ + 5, /* 0xe6 */ + 5, /* 0xe7 */ + 5, /* 0xe8 */ + 5, /* 0xe9 */ + 5, /* 0xea */ + 5, /* 0xeb */ + 5, /* 0xec */ + 5, /* 0xed */ + 5, /* 0xee */ + 5, /* 0xef */ + 5, /* 0xf0 */ + 5, /* 0xf1 */ + 5, /* 0xf2 */ + 5, /* 0xf3 */ + 5, /* 0xf4 */ + 5, /* 0xf5 */ + 5, /* 0xf6 */ + 5, /* 0xf7 */ + 5, /* 0xf8 */ + 5, /* 0xf9 */ + 5, /* 0xfa */ + 5, /* 0xfb */ + 5, /* 0xfc */ + 5, /* 0xfd */ + 5, /* 0xfe */ + 5 /* 0xff */ +}; + +static unsigned char gfxfont_5x8_data[] = { + /* 0x00 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x01 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x02 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x03 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x04 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x05 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x06 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x07 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x08 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x09 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x0a ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x0b ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x0c ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x0d ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x0e ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x0f ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x10 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x11 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x12 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x13 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x14 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x15 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x16 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x17 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x18 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x19 ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x1a ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x1b ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x1c ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x1d ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x1e ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x1f ('.') */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x10, /* ......##.. */ + 0x80, /* ##........ */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0x20 (' ') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x21 ('!') */ + 0x00, /* .... */ + 0x80, /* ##.. */ + 0x80, /* ##.. */ + 0x80, /* ##.. */ + 0x80, /* ##.. */ + 0x00, /* .... */ + 0x80, /* ##.. */ + 0x00, /* .... */ + /* 0x22 ('"') */ + 0x00, /* ........ */ + 0xa0, /* ##..##.. */ + 0xa0, /* ##..##.. */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + /* 0x23 ('#') */ + 0x50, /* ..##..##.... */ + 0x50, /* ..##..##.... */ + 0xf8, /* ##########.. */ + 0x50, /* ..##..##.... */ + 0xf8, /* ##########.. */ + 0x50, /* ..##..##.... */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + /* 0x24 ('$') */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0xa0, /* ##..##...... */ + 0x70, /* ..######.... */ + 0x28, /* ....##..##.. */ + 0x70, /* ..######.... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + /* 0x25 ('%') */ + 0x00, /* ............ */ + 0xc8, /* ####....##.. */ + 0x90, /* ##....##.... */ + 0x20, /* ....##...... */ + 0x48, /* ..##....##.. */ + 0x98, /* ##....####.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x26 ('&') */ + 0x40, /* ..##........ */ + 0xa0, /* ##..##...... */ + 0xa0, /* ##..##...... */ + 0x40, /* ..##........ */ + 0xb8, /* ##..######.. */ + 0x90, /* ##....##.... */ + 0x68, /* ..####..##.. */ + 0x00, /* ............ */ + /* 0x27 (''') */ + 0x00, /* .... */ + 0x80, /* ##.. */ + 0x80, /* ##.. */ + 0x00, /* .... */ + 0x00, /* .... */ + 0x00, /* .... */ + 0x00, /* .... */ + 0x00, /* .... */ + /* 0x28 ('(') */ + 0x00, /* ...... */ + 0x40, /* ..##.. */ + 0x80, /* ##.... */ + 0x80, /* ##.... */ + 0x80, /* ##.... */ + 0x80, /* ##.... */ + 0x40, /* ..##.. */ + 0x00, /* ...... */ + /* 0x29 (')') */ + 0x00, /* ...... */ + 0x80, /* ##.... */ + 0x40, /* ..##.. */ + 0x40, /* ..##.. */ + 0x40, /* ..##.. */ + 0x40, /* ..##.. */ + 0x80, /* ##.... */ + 0x00, /* ...... */ + /* 0x2a ('*') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0xf0, /* ########.. */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0x2b ('+') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0xf8, /* ##########.. */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + /* 0x2c (',') */ + 0x00, /* ...... */ + 0x00, /* ...... */ + 0x00, /* ...... */ + 0x00, /* ...... */ + 0x00, /* ...... */ + 0x40, /* ..##.. */ + 0x40, /* ..##.. */ + 0x80, /* ##.... */ + /* 0x2d ('-') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x2e ('.') */ + 0x00, /* .... */ + 0x00, /* .... */ + 0x00, /* .... */ + 0x00, /* .... */ + 0x00, /* .... */ + 0x00, /* .... */ + 0x80, /* ##.. */ + 0x00, /* .... */ + /* 0x2f ('/') */ + 0x00, /* .............. */ + 0x04, /* ..........##.. */ + 0x08, /* ........##.... */ + 0x10, /* ......##...... */ + 0x20, /* ....##........ */ + 0x40, /* ..##.......... */ + 0x80, /* ##............ */ + 0x00, /* .............. */ + /* 0x30 ('0') */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0x31 ('1') */ + 0x00, /* ........ */ + 0x40, /* ..##.... */ + 0xc0, /* ####.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0xe0, /* ######.. */ + 0x00, /* ........ */ + /* 0x32 ('2') */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x10, /* ......##.. */ + 0x60, /* ..####.... */ + 0x80, /* ##........ */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + /* 0x33 ('3') */ + 0x00, /* .......... */ + 0xf0, /* ########.. */ + 0x20, /* ....##.... */ + 0x60, /* ..####.... */ + 0x10, /* ......##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0x34 ('4') */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x60, /* ..####.... */ + 0xa0, /* ##..##.... */ + 0xf0, /* ########.. */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + /* 0x35 ('5') */ + 0x00, /* .......... */ + 0xf0, /* ########.. */ + 0x80, /* ##........ */ + 0xe0, /* ######.... */ + 0x10, /* ......##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0x36 ('6') */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x80, /* ##........ */ + 0xe0, /* ######.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0x37 ('7') */ + 0x00, /* .......... */ + 0xf0, /* ########.. */ + 0x10, /* ......##.. */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0x40, /* ..##...... */ + 0x00, /* .......... */ + /* 0x38 ('8') */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0x39 ('9') */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x10, /* ......##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0x3a (':') */ + 0x00, /* .... */ + 0x00, /* .... */ + 0x80, /* ##.. */ + 0x00, /* .... */ + 0x00, /* .... */ + 0x80, /* ##.. */ + 0x00, /* .... */ + 0x00, /* .... */ + /* 0x3b (';') */ + 0x00, /* ...... */ + 0x00, /* ...... */ + 0x40, /* ..##.. */ + 0x00, /* ...... */ + 0x00, /* ...... */ + 0x40, /* ..##.. */ + 0x40, /* ..##.. */ + 0x80, /* ##.... */ + /* 0x3c ('<') */ + 0x00, /* ........ */ + 0x20, /* ....##.. */ + 0x40, /* ..##.... */ + 0x80, /* ##...... */ + 0x80, /* ##...... */ + 0x40, /* ..##.... */ + 0x20, /* ....##.. */ + 0x00, /* ........ */ + /* 0x3d ('=') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x3e ('>') */ + 0x00, /* ........ */ + 0x80, /* ##...... */ + 0x40, /* ..##.... */ + 0x20, /* ....##.. */ + 0x20, /* ....##.. */ + 0x40, /* ..##.... */ + 0x80, /* ##...... */ + 0x00, /* ........ */ + /* 0x3f ('?') */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x10, /* ......##.. */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + /* 0x40 ('@') */ + 0x30, /* ....####.... */ + 0x48, /* ..##....##.. */ + 0x98, /* ##....####.. */ + 0xa8, /* ##..##..##.. */ + 0xa8, /* ##..##..##.. */ + 0x90, /* ##....##.... */ + 0x40, /* ..##........ */ + 0x30, /* ....####.... */ + /* 0x41 ('A') */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0xf0, /* ########.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0x42 ('B') */ + 0x00, /* .......... */ + 0xe0, /* ######.... */ + 0x90, /* ##....##.. */ + 0xe0, /* ######.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0xe0, /* ######.... */ + 0x00, /* .......... */ + /* 0x43 ('C') */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0x44 ('D') */ + 0x00, /* .......... */ + 0xe0, /* ######.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0xe0, /* ######.... */ + 0x00, /* .......... */ + /* 0x45 ('E') */ + 0x00, /* .......... */ + 0xf0, /* ########.. */ + 0x80, /* ##........ */ + 0xe0, /* ######.... */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + /* 0x46 ('F') */ + 0x00, /* .......... */ + 0xf0, /* ########.. */ + 0x80, /* ##........ */ + 0xe0, /* ######.... */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0x00, /* .......... */ + /* 0x47 ('G') */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x80, /* ##........ */ + 0xb0, /* ##..####.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0x48 ('H') */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0xf0, /* ########.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0x49 ('I') */ + 0x00, /* ........ */ + 0xe0, /* ######.. */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0xe0, /* ######.. */ + 0x00, /* ........ */ + /* 0x4a ('J') */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0xa0, /* ##..##.... */ + 0x40, /* ..##...... */ + 0x00, /* .......... */ + /* 0x4b ('K') */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0xa0, /* ##..##.... */ + 0xc0, /* ####...... */ + 0xa0, /* ##..##.... */ + 0xa0, /* ##..##.... */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0x4c ('L') */ + 0x00, /* .......... */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + /* 0x4d ('M') */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0xd8, /* ####..####.. */ + 0xa8, /* ##..##..##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + /* 0x4e ('N') */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0xd0, /* ####..##.. */ + 0xd0, /* ####..##.. */ + 0xb0, /* ##..####.. */ + 0xb0, /* ##..####.. */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0x4f ('O') */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0x50 ('P') */ + 0x00, /* .......... */ + 0xe0, /* ######.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0xe0, /* ######.... */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0x00, /* .......... */ + /* 0x51 ('Q') */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0xb0, /* ##..####.. */ + 0x60, /* ..####.... */ + 0x10, /* ......##.. */ + /* 0x52 ('R') */ + 0x00, /* .......... */ + 0xe0, /* ######.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0xe0, /* ######.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0x53 ('S') */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x40, /* ..##...... */ + 0x20, /* ....##.... */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0x54 ('T') */ + 0x00, /* ........ */ + 0xe0, /* ######.. */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x00, /* ........ */ + /* 0x55 ('U') */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0x56 ('V') */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0xa0, /* ##..##.... */ + 0x40, /* ..##...... */ + 0x00, /* .......... */ + /* 0x57 ('W') */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xa8, /* ##..##..##.. */ + 0xd8, /* ####..####.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + /* 0x58 ('X') */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0x59 ('Y') */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + /* 0x5a ('Z') */ + 0x00, /* .......... */ + 0xf0, /* ########.. */ + 0x10, /* ......##.. */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0x80, /* ##........ */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + /* 0x5b ('[') */ + 0x00, /* ........ */ + 0xe0, /* ######.. */ + 0x80, /* ##...... */ + 0x80, /* ##...... */ + 0x80, /* ##...... */ + 0x80, /* ##...... */ + 0xe0, /* ######.. */ + 0x00, /* ........ */ + /* 0x5c ('\') */ + 0x00, /* .............. */ + 0x80, /* ##............ */ + 0x40, /* ..##.......... */ + 0x20, /* ....##........ */ + 0x10, /* ......##...... */ + 0x08, /* ........##.... */ + 0x04, /* ..........##.. */ + 0x00, /* .............. */ + /* 0x5d (']') */ + 0x00, /* ........ */ + 0xe0, /* ######.. */ + 0x20, /* ....##.. */ + 0x20, /* ....##.. */ + 0x20, /* ....##.. */ + 0x20, /* ....##.. */ + 0xe0, /* ######.. */ + 0x00, /* ........ */ + /* 0x5e ('^') */ + 0x00, /* ........ */ + 0x40, /* ..##.... */ + 0xa0, /* ##..##.. */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + /* 0x5f ('_') */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0xf0, /* ######## */ + /* 0x60 ('`') */ + 0x00, /* ...... */ + 0x80, /* ##.... */ + 0x40, /* ..##.. */ + 0x00, /* ...... */ + 0x00, /* ...... */ + 0x00, /* ...... */ + 0x00, /* ...... */ + 0x00, /* ...... */ + /* 0x61 ('a') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0x62 ('b') */ + 0x00, /* .......... */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0xe0, /* ######.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0xe0, /* ######.... */ + 0x00, /* .......... */ + /* 0x63 ('c') */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x60, /* ..####.. */ + 0x80, /* ##...... */ + 0x80, /* ##...... */ + 0x60, /* ..####.. */ + 0x00, /* ........ */ + /* 0x64 ('d') */ + 0x00, /* .......... */ + 0x10, /* ......##.. */ + 0x10, /* ......##.. */ + 0x70, /* ..######.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0x65 ('e') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0xb0, /* ##..####.. */ + 0xc0, /* ####...... */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0x66 ('f') */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x50, /* ..##..##.. */ + 0x40, /* ..##...... */ + 0xe0, /* ######.... */ + 0x40, /* ..##...... */ + 0x40, /* ..##...... */ + 0x00, /* .......... */ + /* 0x67 ('g') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x10, /* ......##.. */ + 0x60, /* ..####.... */ + /* 0x68 ('h') */ + 0x00, /* .......... */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0xe0, /* ######.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0x69 ('i') */ + 0x00, /* ........ */ + 0x40, /* ..##.... */ + 0x00, /* ........ */ + 0xc0, /* ####.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0xe0, /* ######.. */ + 0x00, /* ........ */ + /* 0x6a ('j') */ + 0x00, /* ........ */ + 0x20, /* ....##.. */ + 0x00, /* ........ */ + 0x20, /* ....##.. */ + 0x20, /* ....##.. */ + 0x20, /* ....##.. */ + 0xa0, /* ##..##.. */ + 0x40, /* ..##.... */ + /* 0x6b ('k') */ + 0x00, /* .......... */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0x90, /* ##....##.. */ + 0xe0, /* ######.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0x6c ('l') */ + 0x00, /* ........ */ + 0xc0, /* ####.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0xe0, /* ######.. */ + 0x00, /* ........ */ + /* 0x6d ('m') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0xd0, /* ####..##.... */ + 0xa8, /* ##..##..##.. */ + 0xa8, /* ##..##..##.. */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + /* 0x6e ('n') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0xe0, /* ######.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0x6f ('o') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0x70 ('p') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0xe0, /* ######.... */ + 0x90, /* ##....##.. */ + 0xe0, /* ######.... */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + /* 0x71 ('q') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x10, /* ......##.. */ + 0x10, /* ......##.. */ + /* 0x72 ('r') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0xd0, /* ####..##.. */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0x00, /* .......... */ + /* 0x73 ('s') */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x60, /* ..####.. */ + 0xc0, /* ####.... */ + 0x20, /* ....##.. */ + 0xc0, /* ####.... */ + 0x00, /* ........ */ + /* 0x74 ('t') */ + 0x00, /* .......... */ + 0x40, /* ..##...... */ + 0x40, /* ..##...... */ + 0xe0, /* ######.... */ + 0x40, /* ..##...... */ + 0x50, /* ..##..##.. */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + /* 0x75 ('u') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0x76 ('v') */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0xa0, /* ##..##.. */ + 0xa0, /* ##..##.. */ + 0xa0, /* ##..##.. */ + 0x40, /* ..##.... */ + 0x00, /* ........ */ + /* 0x77 ('w') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0xa8, /* ##..##..##.. */ + 0xa8, /* ##..##..##.. */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + /* 0x78 ('x') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0x79 ('y') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + /* 0x7a ('z') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0xf0, /* ########.. */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + /* 0x7b ('{') */ + 0x30, /* ....####.. */ + 0x40, /* ..##...... */ + 0x20, /* ....##.... */ + 0xc0, /* ####...... */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0x30, /* ....####.. */ + 0x00, /* .......... */ + /* 0x7c ('|') */ + 0x00, /* .... */ + 0x80, /* ##.. */ + 0x80, /* ##.. */ + 0x80, /* ##.. */ + 0x80, /* ##.. */ + 0x80, /* ##.. */ + 0x80, /* ##.. */ + 0x00, /* .... */ + /* 0x7d ('}') */ + 0xc0, /* ####...... */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0x30, /* ....####.. */ + 0x40, /* ..##...... */ + 0x20, /* ....##.... */ + 0xc0, /* ####...... */ + 0x00, /* .......... */ + /* 0x7e ('~') */ + 0x00, /* .......... */ + 0x50, /* ..##..##.. */ + 0xa0, /* ##..##.... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x7f ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x80 ('.') */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + /* 0x81 ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x70, /* ..######.. */ + 0xa0, /* ##..##.... */ + 0xa0, /* ##..##.... */ + 0x70, /* ..######.. */ + 0x20, /* ....##.... */ + /* 0x82 ('.') */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x50, /* ..##..##.. */ + 0xe0, /* ######.... */ + 0x40, /* ..##...... */ + 0x50, /* ..##..##.. */ + 0xa0, /* ##..##.... */ + 0x00, /* .......... */ + /* 0x83 ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x88, /* ##......## */ + 0x70, /* ..######.. */ + 0x50, /* ..##..##.. */ + 0x70, /* ..######.. */ + 0x88, /* ##......## */ + 0x00, /* .......... */ + /* 0x84 ('.') */ + 0x00, /* .......... */ + 0x88, /* ##......## */ + 0x50, /* ..##..##.. */ + 0xf8, /* ########## */ + 0x20, /* ....##.... */ + 0xf8, /* ########## */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + /* 0x85 ('.') */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + /* 0x86 ('.') */ + 0x70, /* ..######.. */ + 0x80, /* ##........ */ + 0xe0, /* ######.... */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x10, /* ......##.. */ + 0xe0, /* ######.... */ + 0x00, /* .......... */ + /* 0x87 ('.') */ + 0x00, /* .......... */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x88 ('.') */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0xa8, /* ##..##..## */ + 0xc8, /* ####....## */ + 0xc8, /* ####....## */ + 0xa8, /* ##..##..## */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0x89 ('.') */ + 0x30, /* ....####.. */ + 0x50, /* ..##..##.. */ + 0x30, /* ....####.. */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x8a ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x50, /* ..##..##.. */ + 0xa0, /* ##..##.... */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x8b ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x10, /* ......##.. */ + 0x10, /* ......##.. */ + 0x00, /* .......... */ + /* 0x8c ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x8d ('.') */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0xe8, /* ######..## */ + 0xd8, /* ####..#### */ + 0xe8, /* ######..## */ + 0xd8, /* ####..#### */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0x8e ('.') */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x8f ('.') */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x50, /* ..##..##.. */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x90 ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x70, /* ..######.. */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0x91 ('.') */ + 0x20, /* ....##.... */ + 0x50, /* ..##..##.. */ + 0x10, /* ......##.. */ + 0x20, /* ....##.... */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x92 ('.') */ + 0x60, /* ..####.... */ + 0x10, /* ......##.. */ + 0x60, /* ..####.... */ + 0x10, /* ......##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x93 ('.') */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x94 ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0xe0, /* ######.... */ + 0x80, /* ##........ */ + /* 0x95 ('.') */ + 0x00, /* .......... */ + 0x78, /* ..######## */ + 0xe8, /* ######..## */ + 0xe8, /* ######..## */ + 0x68, /* ..####..## */ + 0x28, /* ....##..## */ + 0x28, /* ....##..## */ + 0x00, /* .......... */ + /* 0x96 ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x97 ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + /* 0x98 ('.') */ + 0x20, /* ....##.... */ + 0x60, /* ..####.... */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x99 ('.') */ + 0x20, /* ....##.... */ + 0x50, /* ..##..##.. */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x9a ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0xa0, /* ##..##.... */ + 0x50, /* ..##..##.. */ + 0xa0, /* ##..##.... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x9b ('.') */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0xa0, /* ##..##.... */ + 0x60, /* ..####.... */ + 0xf0, /* ########.. */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + /* 0x9c ('.') */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0xa0, /* ##..##.... */ + 0xd0, /* ####..##.. */ + 0x10, /* ......##.. */ + 0x20, /* ....##.... */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0x9d ('.') */ + 0x80, /* ##........ */ + 0x40, /* ..##...... */ + 0x80, /* ##........ */ + 0x60, /* ..####.... */ + 0xa0, /* ##..##.... */ + 0xf0, /* ########.. */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + /* 0x9e ('.') */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0x50, /* ..##..##.. */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + /* 0x9f ('.') */ + 0x40, /* ..##...... */ + 0x20, /* ....##.... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0xf0, /* ########.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0xa0 ('.') */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0xf0, /* ########.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0xa1 ('.') */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0xf0, /* ########.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0xa2 ('.') */ + 0x50, /* ..##..##.. */ + 0xa0, /* ##..##.... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0xf0, /* ########.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0xa3 ('.') */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0xf0, /* ########.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0xa4 ('.') */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0xf0, /* ########.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0xa5 ('.') */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0xa0, /* ##..##.... */ + 0xa0, /* ##..##.... */ + 0xf0, /* ########.. */ + 0xa0, /* ##..##.... */ + 0xb0, /* ##..####.. */ + 0x00, /* .......... */ + /* 0xa6 ('.') */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x40, /* ..##...... */ + /* 0xa7 ('.') */ + 0x40, /* ..##...... */ + 0x20, /* ....##.... */ + 0xf0, /* ########.. */ + 0x80, /* ##........ */ + 0xe0, /* ######.... */ + 0x80, /* ##........ */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + /* 0xa8 ('.') */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0xf0, /* ########.. */ + 0x80, /* ##........ */ + 0xe0, /* ######.... */ + 0x80, /* ##........ */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + /* 0xa9 ('.') */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0xf0, /* ########.. */ + 0x80, /* ##........ */ + 0xe0, /* ######.... */ + 0x80, /* ##........ */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + /* 0xaa ('.') */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + 0xf0, /* ########.. */ + 0x80, /* ##........ */ + 0xe0, /* ######.... */ + 0x80, /* ##........ */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + /* 0xab ('.') */ + 0x40, /* ..##...... */ + 0x20, /* ....##.... */ + 0x70, /* ..######.. */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xac ('.') */ + 0x10, /* ......##.. */ + 0x20, /* ....##.... */ + 0x70, /* ..######.. */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xad ('.') */ + 0x20, /* ....##.... */ + 0x50, /* ..##..##.. */ + 0x70, /* ..######.. */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xae ('.') */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xaf ('.') */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x48, /* ..##....## */ + 0xe8, /* ######..## */ + 0x48, /* ..##....## */ + 0x48, /* ..##....## */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xb0 ('.') */ + 0x50, /* ..##..##.. */ + 0xa0, /* ##..##.... */ + 0x90, /* ##....##.. */ + 0xd0, /* ####..##.. */ + 0xb0, /* ##..####.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0xb1 ('.') */ + 0x40, /* ..##...... */ + 0x20, /* ....##.... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xb2 ('.') */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xb3 ('.') */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xb4 ('.') */ + 0x50, /* ..##..##.. */ + 0xa0, /* ##..##.... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xb5 ('.') */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xb6 ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x50, /* ..##..##.. */ + 0x20, /* ....##.... */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + /* 0xb7 ('.') */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0xb0, /* ##..####.. */ + 0xb0, /* ##..####.. */ + 0xd0, /* ####..##.. */ + 0xd0, /* ####..##.. */ + 0xe0, /* ######.... */ + 0x00, /* .......... */ + /* 0xb8 ('.') */ + 0x40, /* ..##...... */ + 0x20, /* ....##.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xb9 ('.') */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xba ('.') */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xbb ('.') */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xbc ('.') */ + 0x10, /* ......##.. */ + 0x20, /* ....##.... */ + 0x88, /* ##......## */ + 0x50, /* ..##..##.. */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + /* 0xbd ('.') */ + 0x00, /* .......... */ + 0x80, /* ##........ */ + 0xe0, /* ######.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0xe0, /* ######.... */ + 0x80, /* ##........ */ + 0x00, /* .......... */ + /* 0xbe ('.') */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0xa0, /* ##..##.... */ + 0xa0, /* ##..##.... */ + 0x90, /* ##....##.. */ + 0xa0, /* ##..##.... */ + 0x00, /* .......... */ + /* 0xbf ('.') */ + 0x40, /* ..##...... */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xc0 ('.') */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xc1 ('.') */ + 0x20, /* ....##.... */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xc2 ('.') */ + 0x50, /* ..##..##.. */ + 0xa0, /* ##..##.... */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xc3 ('.') */ + 0x00, /* .......... */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xc4 ('.') */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x70, /* ..######.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xc5 ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0xf0, /* ########.. */ + 0x68, /* ..####..## */ + 0xb0, /* ##..####.. */ + 0x78, /* ..######## */ + 0x00, /* .......... */ + /* 0xc6 ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x30, /* ....####.. */ + 0x40, /* ..##...... */ + 0x40, /* ..##...... */ + 0x30, /* ....####.. */ + 0x20, /* ....##.... */ + /* 0xc7 ('.') */ + 0x40, /* ..##...... */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0xb0, /* ##..####.. */ + 0xc0, /* ####...... */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xc8 ('.') */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0xb0, /* ##..####.. */ + 0xc0, /* ####...... */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xc9 ('.') */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0xb0, /* ##..####.. */ + 0xc0, /* ####...... */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xca ('.') */ + 0x00, /* .......... */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0xb0, /* ##..####.. */ + 0xc0, /* ####...... */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xcb ('.') */ + 0x40, /* ..##...... */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xcc ('.') */ + 0x10, /* ......##.. */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xcd ('.') */ + 0x20, /* ....##.... */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xce ('.') */ + 0x00, /* .......... */ + 0x50, /* ..##..##.. */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x20, /* ....##.... */ + 0x20, /* ....##.... */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xcf ('.') */ + 0xa0, /* ##..##.... */ + 0x40, /* ..##...... */ + 0xa0, /* ##..##.... */ + 0x10, /* ......##.. */ + 0x70, /* ..######.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xd0 ('.') */ + 0x50, /* ..##..##.. */ + 0xa0, /* ##..##.... */ + 0x00, /* .......... */ + 0xe0, /* ######.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0xd1 ('.') */ + 0x40, /* ..##...... */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xd2 ('.') */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xd3 ('.') */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xd4 ('.') */ + 0x50, /* ..##..##.. */ + 0xa0, /* ##..##.... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xd5 ('.') */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xd6 ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + /* 0xd7 ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0xb0, /* ##..####.. */ + 0xd0, /* ####..##.. */ + 0xe0, /* ######.... */ + 0x00, /* .......... */ + /* 0xd8 ('.') */ + 0x40, /* ..##...... */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xd9 ('.') */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xda ('.') */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xdb ('.') */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xdc ('.') */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + /* 0xdd ('.') */ + 0x00, /* .......... */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0xe0, /* ######.... */ + 0x90, /* ##....##.. */ + 0xe0, /* ######.... */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + /* 0xde ('.') */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + /* 0xdf ('.') */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0xf0, /* ########.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0xe0 ('.') */ + 0x00, /* .......... */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xe1 ('.') */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0xf0, /* ########.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + /* 0xe2 ('.') */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xe3 ('.') */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0xf0, /* ########.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x30, /* ....####.. */ + /* 0xe4 ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x20, /* ....##.... */ + /* 0xe5 ('.') */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x80, /* ##........ */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xe6 ('.') */ + 0x10, /* ......##.. */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + 0x30, /* ....####.. */ + 0x40, /* ..##...... */ + 0x40, /* ..##...... */ + 0x30, /* ....####.. */ + 0x00, /* .......... */ + /* 0xe7 ('.') */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x80, /* ##........ */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xe8 ('.') */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xe9 ('.') */ + 0x40, /* ..##...... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x80, /* ##........ */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xea ('.') */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x00, /* .......... */ + 0x30, /* ....####.. */ + 0x40, /* ..##...... */ + 0x40, /* ..##...... */ + 0x30, /* ....####.. */ + 0x00, /* .......... */ + /* 0xeb ('.') */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x80, /* ##........ */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xec ('.') */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xed ('.') */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0xe0, /* ######.... */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0xe0, /* ######.... */ + 0x00, /* .......... */ + /* 0xee ('.') */ + 0xa0, /* ##..##.... */ + 0x50, /* ..##..##.. */ + 0x10, /* ......##.. */ + 0x70, /* ..######.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xef ('.') */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x48, /* ..##....## */ + 0xe8, /* ######..## */ + 0x48, /* ..##....## */ + 0x48, /* ..##....## */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + /* 0xf0 ('.') */ + 0x00, /* .......... */ + 0x20, /* ....##.... */ + 0x70, /* ..######.. */ + 0x20, /* ....##.... */ + 0x60, /* ..####.... */ + 0xa0, /* ##..##.... */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xf1 ('.') */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + 0xf0, /* ########.. */ + 0x80, /* ##........ */ + 0xe0, /* ######.... */ + 0x80, /* ##........ */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + /* 0xf2 ('.') */ + 0x00, /* .......... */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0xb0, /* ##..####.. */ + 0xc0, /* ####...... */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xf3 ('.') */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0xf0, /* ########.. */ + 0x80, /* ##........ */ + 0xe0, /* ######.... */ + 0x80, /* ##........ */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + /* 0xf4 ('.') */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0xb0, /* ##..####.. */ + 0xc0, /* ####...... */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xf5 ('.') */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + 0xf0, /* ########.. */ + 0x80, /* ##........ */ + 0xe0, /* ######.... */ + 0x80, /* ##........ */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + /* 0xf6 ('.') */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0xb0, /* ##..####.. */ + 0xc0, /* ####...... */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xf7 ('.') */ + 0x00, /* .......... */ + 0xf0, /* ########.. */ + 0x80, /* ##........ */ + 0xe0, /* ######.... */ + 0x80, /* ##........ */ + 0x80, /* ##........ */ + 0xf0, /* ########.. */ + 0x20, /* ....##.... */ + /* 0xf8 ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0xb0, /* ##..####.. */ + 0xc0, /* ####...... */ + 0x60, /* ..####.... */ + 0x20, /* ....##.... */ + /* 0xf9 ('.') */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0xf0, /* ########.. */ + 0x80, /* ##........ */ + 0xe0, /* ######.... */ + 0x80, /* ##........ */ + 0xf0, /* ########.. */ + 0x00, /* .......... */ + /* 0xfa ('.') */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0xb0, /* ##..####.. */ + 0xc0, /* ####...... */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xfb ('.') */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x80, /* ##........ */ + 0xb0, /* ##..####.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xfc ('.') */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x10, /* ......##.. */ + 0x60, /* ..####.... */ + /* 0xfd ('.') */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x70, /* ..######.. */ + 0x80, /* ##........ */ + 0xb0, /* ##..####.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + /* 0xfe ('.') */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x90, /* ##....##.. */ + 0x70, /* ..######.. */ + 0x10, /* ......##.. */ + 0x60, /* ..####.... */ + /* 0xff ('.') */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x80, /* ##........ */ + 0xb0, /* ##..####.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + 0x00 /* .......... */ +}; + +gfx_bitmap_font_t gfxfont_5x8 = { + -1, /* resource ID */ + 256, /* # of characters */ + gfxfont_5x8_widths, /* Widths */ + 1, /* Bytes per row */ + 9, /* Line height */ + 8, /* Char height */ + 8, /* Char size (occupied, in bytes) */ + gfxfont_5x8_data /* Bulk data */ +}; diff --git a/engines/sci/gfx/font-6x10.c b/engines/sci/gfx/font-6x10.c new file mode 100644 index 0000000000..11919c00bd --- /dev/null +++ b/engines/sci/gfx/font-6x10.c @@ -0,0 +1,3092 @@ +/* Auto-generated by bdftofont.c */ + +#include + +static int gfxfont_6x10_widths[] = { + 6, /* 0x00 */ + 6, /* 0x01 */ + 6, /* 0x02 */ + 6, /* 0x03 */ + 6, /* 0x04 */ + 6, /* 0x05 */ + 6, /* 0x06 */ + 6, /* 0x07 */ + 6, /* 0x08 */ + 6, /* 0x09 */ + 6, /* 0x0a */ + 6, /* 0x0b */ + 6, /* 0x0c */ + 6, /* 0x0d */ + 6, /* 0x0e */ + 6, /* 0x0f */ + 6, /* 0x10 */ + 6, /* 0x11 */ + 6, /* 0x12 */ + 6, /* 0x13 */ + 6, /* 0x14 */ + 6, /* 0x15 */ + 6, /* 0x16 */ + 6, /* 0x17 */ + 6, /* 0x18 */ + 6, /* 0x19 */ + 6, /* 0x1a */ + 6, /* 0x1b */ + 6, /* 0x1c */ + 6, /* 0x1d */ + 6, /* 0x1e */ + 6, /* 0x1f */ + 4, /* 0x20 */ + 4, /* 0x21 */ + 4, /* 0x22 */ + 6, /* 0x23 */ + 6, /* 0x24 */ + 6, /* 0x25 */ + 6, /* 0x26 */ + 2, /* 0x27 */ + 5, /* 0x28 */ + 5, /* 0x29 */ + 6, /* 0x2a */ + 6, /* 0x2b */ + 4, /* 0x2c */ + 6, /* 0x2d */ + 5, /* 0x2e */ + 7, /* 0x2f */ + 6, /* 0x30 */ + 4, /* 0x31 */ + 6, /* 0x32 */ + 6, /* 0x33 */ + 6, /* 0x34 */ + 6, /* 0x35 */ + 6, /* 0x36 */ + 6, /* 0x37 */ + 6, /* 0x38 */ + 6, /* 0x39 */ + 5, /* 0x3a */ + 5, /* 0x3b */ + 6, /* 0x3c */ + 6, /* 0x3d */ + 6, /* 0x3e */ + 6, /* 0x3f */ + 6, /* 0x40 */ + 6, /* 0x41 */ + 6, /* 0x42 */ + 6, /* 0x43 */ + 6, /* 0x44 */ + 6, /* 0x45 */ + 6, /* 0x46 */ + 6, /* 0x47 */ + 6, /* 0x48 */ + 4, /* 0x49 */ + 6, /* 0x4a */ + 6, /* 0x4b */ + 6, /* 0x4c */ + 6, /* 0x4d */ + 6, /* 0x4e */ + 6, /* 0x4f */ + 6, /* 0x50 */ + 6, /* 0x51 */ + 6, /* 0x52 */ + 6, /* 0x53 */ + 6, /* 0x54 */ + 6, /* 0x55 */ + 6, /* 0x56 */ + 6, /* 0x57 */ + 6, /* 0x58 */ + 6, /* 0x59 */ + 6, /* 0x5a */ + 5, /* 0x5b */ + 7, /* 0x5c */ + 5, /* 0x5d */ + 6, /* 0x5e */ + 5, /* 0x5f */ + 3, /* 0x60 */ + 6, /* 0x61 */ + 6, /* 0x62 */ + 6, /* 0x63 */ + 6, /* 0x64 */ + 6, /* 0x65 */ + 6, /* 0x66 */ + 6, /* 0x67 */ + 6, /* 0x68 */ + 4, /* 0x69 */ + 5, /* 0x6a */ + 6, /* 0x6b */ + 4, /* 0x6c */ + 6, /* 0x6d */ + 6, /* 0x6e */ + 6, /* 0x6f */ + 6, /* 0x70 */ + 6, /* 0x71 */ + 6, /* 0x72 */ + 6, /* 0x73 */ + 6, /* 0x74 */ + 6, /* 0x75 */ + 6, /* 0x76 */ + 6, /* 0x77 */ + 6, /* 0x78 */ + 6, /* 0x79 */ + 6, /* 0x7a */ + 6, /* 0x7b */ + 3, /* 0x7c */ + 6, /* 0x7d */ + 6, /* 0x7e */ + 6, /* 0x7f */ + 6, /* 0x80 */ + 6, /* 0x81 */ + 6, /* 0x82 */ + 6, /* 0x83 */ + 6, /* 0x84 */ + 6, /* 0x85 */ + 6, /* 0x86 */ + 6, /* 0x87 */ + 6, /* 0x88 */ + 6, /* 0x89 */ + 6, /* 0x8a */ + 6, /* 0x8b */ + 6, /* 0x8c */ + 6, /* 0x8d */ + 6, /* 0x8e */ + 6, /* 0x8f */ + 6, /* 0x90 */ + 6, /* 0x91 */ + 6, /* 0x92 */ + 6, /* 0x93 */ + 6, /* 0x94 */ + 6, /* 0x95 */ + 6, /* 0x96 */ + 6, /* 0x97 */ + 6, /* 0x98 */ + 6, /* 0x99 */ + 6, /* 0x9a */ + 6, /* 0x9b */ + 6, /* 0x9c */ + 6, /* 0x9d */ + 6, /* 0x9e */ + 6, /* 0x9f */ + 6, /* 0xa0 */ + 6, /* 0xa1 */ + 6, /* 0xa2 */ + 6, /* 0xa3 */ + 6, /* 0xa4 */ + 6, /* 0xa5 */ + 6, /* 0xa6 */ + 6, /* 0xa7 */ + 6, /* 0xa8 */ + 6, /* 0xa9 */ + 6, /* 0xaa */ + 6, /* 0xab */ + 6, /* 0xac */ + 6, /* 0xad */ + 6, /* 0xae */ + 6, /* 0xaf */ + 6, /* 0xb0 */ + 6, /* 0xb1 */ + 6, /* 0xb2 */ + 6, /* 0xb3 */ + 6, /* 0xb4 */ + 6, /* 0xb5 */ + 6, /* 0xb6 */ + 6, /* 0xb7 */ + 6, /* 0xb8 */ + 6, /* 0xb9 */ + 6, /* 0xba */ + 6, /* 0xbb */ + 6, /* 0xbc */ + 6, /* 0xbd */ + 6, /* 0xbe */ + 6, /* 0xbf */ + 6, /* 0xc0 */ + 6, /* 0xc1 */ + 6, /* 0xc2 */ + 6, /* 0xc3 */ + 6, /* 0xc4 */ + 6, /* 0xc5 */ + 6, /* 0xc6 */ + 6, /* 0xc7 */ + 6, /* 0xc8 */ + 6, /* 0xc9 */ + 6, /* 0xca */ + 6, /* 0xcb */ + 6, /* 0xcc */ + 6, /* 0xcd */ + 6, /* 0xce */ + 6, /* 0xcf */ + 6, /* 0xd0 */ + 6, /* 0xd1 */ + 6, /* 0xd2 */ + 6, /* 0xd3 */ + 6, /* 0xd4 */ + 6, /* 0xd5 */ + 6, /* 0xd6 */ + 6, /* 0xd7 */ + 6, /* 0xd8 */ + 6, /* 0xd9 */ + 6, /* 0xda */ + 6, /* 0xdb */ + 6, /* 0xdc */ + 6, /* 0xdd */ + 6, /* 0xde */ + 6, /* 0xdf */ + 6, /* 0xe0 */ + 6, /* 0xe1 */ + 6, /* 0xe2 */ + 6, /* 0xe3 */ + 6, /* 0xe4 */ + 6, /* 0xe5 */ + 6, /* 0xe6 */ + 6, /* 0xe7 */ + 6, /* 0xe8 */ + 6, /* 0xe9 */ + 6, /* 0xea */ + 6, /* 0xeb */ + 6, /* 0xec */ + 6, /* 0xed */ + 6, /* 0xee */ + 6, /* 0xef */ + 6, /* 0xf0 */ + 6, /* 0xf1 */ + 6, /* 0xf2 */ + 6, /* 0xf3 */ + 6, /* 0xf4 */ + 6, /* 0xf5 */ + 6, /* 0xf6 */ + 6, /* 0xf7 */ + 6, /* 0xf8 */ + 6, /* 0xf9 */ + 6, /* 0xfa */ + 6, /* 0xfb */ + 6, /* 0xfc */ + 6, /* 0xfd */ + 6, /* 0xfe */ + 6 /* 0xff */ +}; + +static unsigned char gfxfont_6x10_data[] = { + /* 0x00 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x01 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x02 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x03 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x04 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x05 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x06 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x07 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x08 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x09 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x0a ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x0b ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x0c ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x0d ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x0e ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x0f ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x10 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x11 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x12 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x13 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x14 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x15 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x16 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x17 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x18 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x19 ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x1a ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x1b ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x1c ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x1d ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x1e ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x1f ('.') */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0xa8, /* ##..##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x20 (' ') */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + /* 0x21 ('!') */ + 0x00, /* ........ */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x00, /* ........ */ + 0x40, /* ..##.... */ + 0x00, /* ........ */ + 0x00, /* ........ */ + /* 0x22 ('"') */ + 0x00, /* ........ */ + 0xa0, /* ##..##.. */ + 0xa0, /* ##..##.. */ + 0xa0, /* ##..##.. */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + /* 0x23 ('#') */ + 0x00, /* ............ */ + 0x50, /* ..##..##.... */ + 0x50, /* ..##..##.... */ + 0xf8, /* ##########.. */ + 0x50, /* ..##..##.... */ + 0xf8, /* ##########.. */ + 0x50, /* ..##..##.... */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x24 ('$') */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0xa0, /* ##..##...... */ + 0x70, /* ..######.... */ + 0x28, /* ....##..##.. */ + 0x70, /* ..######.... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x25 ('%') */ + 0x00, /* ............ */ + 0x48, /* ..##....##.. */ + 0xa8, /* ##..##..##.. */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0xa8, /* ##..##..##.. */ + 0x90, /* ##....##.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x26 ('&') */ + 0x00, /* ............ */ + 0x40, /* ..##........ */ + 0xa0, /* ##..##...... */ + 0xa0, /* ##..##...... */ + 0x40, /* ..##........ */ + 0xa8, /* ##..##..##.. */ + 0x90, /* ##....##.... */ + 0x68, /* ..####..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x27 (''') */ + 0x00, /* .... */ + 0x80, /* ##.. */ + 0x80, /* ##.. */ + 0x80, /* ##.. */ + 0x00, /* .... */ + 0x00, /* .... */ + 0x00, /* .... */ + 0x00, /* .... */ + 0x00, /* .... */ + 0x00, /* .... */ + /* 0x28 ('(') */ + 0x00, /* .......... */ + 0x10, /* ......##.. */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0x40, /* ..##...... */ + 0x40, /* ..##...... */ + 0x20, /* ....##.... */ + 0x10, /* ......##.. */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x29 (')') */ + 0x00, /* .......... */ + 0x40, /* ..##...... */ + 0x20, /* ....##.... */ + 0x10, /* ......##.. */ + 0x10, /* ......##.. */ + 0x10, /* ......##.. */ + 0x20, /* ....##.... */ + 0x40, /* ..##...... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x2a ('*') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x50, /* ..##..##.... */ + 0xf8, /* ##########.. */ + 0x50, /* ..##..##.... */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x2b ('+') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0xf8, /* ##########.. */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x2c (',') */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x00, /* ........ */ + 0x60, /* ..####.. */ + 0x40, /* ..##.... */ + 0x80, /* ##...... */ + 0x00, /* ........ */ + /* 0x2d ('-') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x2e ('.') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x2f ('/') */ + 0x00, /* .............. */ + 0x02, /* ............## */ + 0x04, /* ..........##.. */ + 0x08, /* ........##.... */ + 0x10, /* ......##...... */ + 0x20, /* ....##........ */ + 0x40, /* ..##.......... */ + 0x80, /* ##............ */ + 0x00, /* .............. */ + 0x00, /* .............. */ + /* 0x30 ('0') */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x31 ('1') */ + 0x00, /* ........ */ + 0x40, /* ..##.... */ + 0xc0, /* ####.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0xe0, /* ######.. */ + 0x00, /* ........ */ + 0x00, /* ........ */ + /* 0x32 ('2') */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x08, /* ........##.. */ + 0x30, /* ....####.... */ + 0x40, /* ..##........ */ + 0x80, /* ##.......... */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x33 ('3') */ + 0x00, /* ............ */ + 0xf8, /* ##########.. */ + 0x08, /* ........##.. */ + 0x10, /* ......##.... */ + 0x30, /* ....####.... */ + 0x08, /* ........##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x34 ('4') */ + 0x00, /* ............ */ + 0x10, /* ......##.... */ + 0x30, /* ....####.... */ + 0x50, /* ..##..##.... */ + 0x90, /* ##....##.... */ + 0xf8, /* ##########.. */ + 0x10, /* ......##.... */ + 0x10, /* ......##.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x35 ('5') */ + 0x00, /* ............ */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0xb0, /* ##..####.... */ + 0xc8, /* ####....##.. */ + 0x08, /* ........##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x36 ('6') */ + 0x00, /* ............ */ + 0x30, /* ....####.... */ + 0x40, /* ..##........ */ + 0x80, /* ##.......... */ + 0xb0, /* ##..####.... */ + 0xc8, /* ####....##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x37 ('7') */ + 0x00, /* ............ */ + 0xf8, /* ##########.. */ + 0x08, /* ........##.. */ + 0x10, /* ......##.... */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x40, /* ..##........ */ + 0x40, /* ..##........ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x38 ('8') */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x39 ('9') */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x98, /* ##....####.. */ + 0x68, /* ..####..##.. */ + 0x08, /* ........##.. */ + 0x10, /* ......##.... */ + 0x60, /* ..####...... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x3a (':') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x3b (';') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x60, /* ..####.... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x60, /* ..####.... */ + 0x40, /* ..##...... */ + 0x80, /* ##........ */ + 0x00, /* .......... */ + /* 0x3c ('<') */ + 0x00, /* ............ */ + 0x08, /* ........##.. */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x40, /* ..##........ */ + 0x20, /* ....##...... */ + 0x10, /* ......##.... */ + 0x08, /* ........##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x3d ('=') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x3e ('>') */ + 0x00, /* ............ */ + 0x40, /* ..##........ */ + 0x20, /* ....##...... */ + 0x10, /* ......##.... */ + 0x08, /* ........##.. */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x40, /* ..##........ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x3f ('?') */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x40 ('@') */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x98, /* ##....####.. */ + 0xa8, /* ##..##..##.. */ + 0xb0, /* ##..####.... */ + 0x80, /* ##.......... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x41 ('A') */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x42 ('B') */ + 0x00, /* ............ */ + 0xf0, /* ########.... */ + 0x48, /* ..##....##.. */ + 0x48, /* ..##....##.. */ + 0x70, /* ..######.... */ + 0x48, /* ..##....##.. */ + 0x48, /* ..##....##.. */ + 0xf0, /* ########.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x43 ('C') */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x44 ('D') */ + 0x00, /* ............ */ + 0xf0, /* ########.... */ + 0x48, /* ..##....##.. */ + 0x48, /* ..##....##.. */ + 0x48, /* ..##....##.. */ + 0x48, /* ..##....##.. */ + 0x48, /* ..##....##.. */ + 0xf0, /* ########.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x45 ('E') */ + 0x00, /* ............ */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xf0, /* ########.... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x46 ('F') */ + 0x00, /* ............ */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xf0, /* ########.... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x47 ('G') */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x98, /* ##....####.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x48 ('H') */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x49 ('I') */ + 0x00, /* ........ */ + 0xe0, /* ######.. */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0xe0, /* ######.. */ + 0x00, /* ........ */ + 0x00, /* ........ */ + /* 0x4a ('J') */ + 0x00, /* ............ */ + 0x38, /* ....######.. */ + 0x10, /* ......##.... */ + 0x10, /* ......##.... */ + 0x10, /* ......##.... */ + 0x10, /* ......##.... */ + 0x90, /* ##....##.... */ + 0x60, /* ..####...... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x4b ('K') */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x90, /* ##....##.... */ + 0xa0, /* ##..##...... */ + 0xc0, /* ####........ */ + 0xa0, /* ##..##...... */ + 0x90, /* ##....##.... */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x4c ('L') */ + 0x00, /* ............ */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x4d ('M') */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xd8, /* ####..####.. */ + 0xa8, /* ##..##..##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x4e ('N') */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xc8, /* ####....##.. */ + 0xa8, /* ##..##..##.. */ + 0x98, /* ##....####.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x4f ('O') */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x50 ('P') */ + 0x00, /* ............ */ + 0xf0, /* ########.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xf0, /* ########.... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x51 ('Q') */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xa8, /* ##..##..##.. */ + 0x70, /* ..######.... */ + 0x08, /* ........##.. */ + 0x00, /* ............ */ + /* 0x52 ('R') */ + 0x00, /* ............ */ + 0xf0, /* ########.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xf0, /* ########.... */ + 0xa0, /* ##..##...... */ + 0x90, /* ##....##.... */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x53 ('S') */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x80, /* ##.......... */ + 0x70, /* ..######.... */ + 0x08, /* ........##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x54 ('T') */ + 0x00, /* ............ */ + 0xf8, /* ##########.. */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x55 ('U') */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x56 ('V') */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x50, /* ..##..##.... */ + 0x50, /* ..##..##.... */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x57 ('W') */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xa8, /* ##..##..##.. */ + 0xa8, /* ##..##..##.. */ + 0xd8, /* ####..####.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x58 ('X') */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x59 ('Y') */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x5a ('Z') */ + 0x00, /* ............ */ + 0xf8, /* ##########.. */ + 0x08, /* ........##.. */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x40, /* ..##........ */ + 0x80, /* ##.......... */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x5b ('[') */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x40, /* ..##...... */ + 0x40, /* ..##...... */ + 0x40, /* ..##...... */ + 0x40, /* ..##...... */ + 0x40, /* ..##...... */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x5c ('\') */ + 0x00, /* .............. */ + 0x80, /* ##............ */ + 0x40, /* ..##.......... */ + 0x20, /* ....##........ */ + 0x10, /* ......##...... */ + 0x08, /* ........##.... */ + 0x04, /* ..........##.. */ + 0x02, /* ............## */ + 0x00, /* .............. */ + 0x00, /* .............. */ + /* 0x5d (']') */ + 0x00, /* .......... */ + 0x70, /* ..######.. */ + 0x10, /* ......##.. */ + 0x10, /* ......##.. */ + 0x10, /* ......##.. */ + 0x10, /* ......##.. */ + 0x10, /* ......##.. */ + 0x70, /* ..######.. */ + 0x00, /* .......... */ + 0x00, /* .......... */ + /* 0x5e ('^') */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x5f ('_') */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0x00, /* .......... */ + 0xf8, /* ########## */ + 0x00, /* .......... */ + /* 0x60 ('`') */ + 0x80, /* ##.... */ + 0x40, /* ..##.. */ + 0x00, /* ...... */ + 0x00, /* ...... */ + 0x00, /* ...... */ + 0x00, /* ...... */ + 0x00, /* ...... */ + 0x00, /* ...... */ + 0x00, /* ...... */ + 0x00, /* ...... */ + /* 0x61 ('a') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x08, /* ........##.. */ + 0x78, /* ..########.. */ + 0x88, /* ##......##.. */ + 0x78, /* ..########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x62 ('b') */ + 0x00, /* ............ */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xb0, /* ##..####.... */ + 0xc8, /* ####....##.. */ + 0x88, /* ##......##.. */ + 0xc8, /* ####....##.. */ + 0xb0, /* ##..####.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x63 ('c') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x80, /* ##.......... */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x64 ('d') */ + 0x00, /* ............ */ + 0x08, /* ........##.. */ + 0x08, /* ........##.. */ + 0x68, /* ..####..##.. */ + 0x98, /* ##....####.. */ + 0x88, /* ##......##.. */ + 0x98, /* ##....####.. */ + 0x68, /* ..####..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x65 ('e') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x66 ('f') */ + 0x00, /* ............ */ + 0x30, /* ....####.... */ + 0x48, /* ..##....##.. */ + 0x40, /* ..##........ */ + 0xf0, /* ########.... */ + 0x40, /* ..##........ */ + 0x40, /* ..##........ */ + 0x40, /* ..##........ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x67 ('g') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x78, /* ..########.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x78, /* ..########.. */ + 0x08, /* ........##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + /* 0x68 ('h') */ + 0x00, /* ............ */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xb0, /* ##..####.... */ + 0xc8, /* ####....##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x69 ('i') */ + 0x00, /* ........ */ + 0x40, /* ..##.... */ + 0x00, /* ........ */ + 0xc0, /* ####.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0xe0, /* ######.. */ + 0x00, /* ........ */ + 0x00, /* ........ */ + /* 0x6a ('j') */ + 0x00, /* .......... */ + 0x10, /* ......##.. */ + 0x00, /* .......... */ + 0x30, /* ....####.. */ + 0x10, /* ......##.. */ + 0x10, /* ......##.. */ + 0x10, /* ......##.. */ + 0x90, /* ##....##.. */ + 0x90, /* ##....##.. */ + 0x60, /* ..####.... */ + /* 0x6b ('k') */ + 0x00, /* ............ */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x88, /* ##......##.. */ + 0x90, /* ##....##.... */ + 0xe0, /* ######...... */ + 0x90, /* ##....##.... */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x6c ('l') */ + 0x00, /* ........ */ + 0xc0, /* ####.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0x40, /* ..##.... */ + 0xe0, /* ######.. */ + 0x00, /* ........ */ + 0x00, /* ........ */ + /* 0x6d ('m') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0xd0, /* ####..##.... */ + 0xa8, /* ##..##..##.. */ + 0xa8, /* ##..##..##.. */ + 0xa8, /* ##..##..##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x6e ('n') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0xb0, /* ##..####.... */ + 0xc8, /* ####....##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x6f ('o') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x70 ('p') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0xb0, /* ##..####.... */ + 0xc8, /* ####....##.. */ + 0x88, /* ##......##.. */ + 0xc8, /* ####....##.. */ + 0xb0, /* ##..####.... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + /* 0x71 ('q') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x68, /* ..####..##.. */ + 0x98, /* ##....####.. */ + 0x88, /* ##......##.. */ + 0x98, /* ##....####.. */ + 0x68, /* ..####..##.. */ + 0x08, /* ........##.. */ + 0x08, /* ........##.. */ + /* 0x72 ('r') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0xb0, /* ##..####.... */ + 0xc8, /* ####....##.. */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x73 ('s') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x80, /* ##.......... */ + 0x70, /* ..######.... */ + 0x08, /* ........##.. */ + 0xf0, /* ########.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x74 ('t') */ + 0x00, /* ............ */ + 0x40, /* ..##........ */ + 0x40, /* ..##........ */ + 0xf0, /* ########.... */ + 0x40, /* ..##........ */ + 0x40, /* ..##........ */ + 0x48, /* ..##....##.. */ + 0x30, /* ....####.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x75 ('u') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x98, /* ##....####.. */ + 0x68, /* ..####..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x76 ('v') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x50, /* ..##..##.... */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x77 ('w') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xa8, /* ##..##..##.. */ + 0xa8, /* ##..##..##.. */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x78 ('x') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x79 ('y') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x98, /* ##....####.. */ + 0x68, /* ..####..##.. */ + 0x08, /* ........##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + /* 0x7a ('z') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0xf8, /* ##########.. */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x40, /* ..##........ */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x7b ('{') */ + 0x00, /* ............ */ + 0x18, /* ......####.. */ + 0x20, /* ....##...... */ + 0x10, /* ......##.... */ + 0x60, /* ..####...... */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x18, /* ......####.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x7c ('|') */ + 0x00, /* ...... */ + 0x40, /* ..##.. */ + 0x40, /* ..##.. */ + 0x40, /* ..##.. */ + 0x40, /* ..##.. */ + 0x40, /* ..##.. */ + 0x40, /* ..##.. */ + 0x40, /* ..##.. */ + 0x00, /* ...... */ + 0x00, /* ...... */ + /* 0x7d ('}') */ + 0x00, /* ............ */ + 0x60, /* ..####...... */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x18, /* ......####.. */ + 0x20, /* ....##...... */ + 0x10, /* ......##.... */ + 0x60, /* ..####...... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x7e ('~') */ + 0x00, /* ............ */ + 0x48, /* ..##....##.. */ + 0xa8, /* ##..##..##.. */ + 0x90, /* ##....##.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x7f ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x80 ('.') */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x81 ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x78, /* ..########.. */ + 0xa0, /* ##..##...... */ + 0xa0, /* ##..##...... */ + 0xa0, /* ##..##...... */ + 0x78, /* ..########.. */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + /* 0x82 ('.') */ + 0x00, /* ............ */ + 0x30, /* ....####.... */ + 0x48, /* ..##....##.. */ + 0x40, /* ..##........ */ + 0xe0, /* ######...... */ + 0x40, /* ..##........ */ + 0x48, /* ..##....##.. */ + 0xb0, /* ##..####.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x83 ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x50, /* ..##..##.... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x84 ('.') */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0xf8, /* ##########.. */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + /* 0x85 ('.') */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x86 ('.') */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x80, /* ##.......... */ + 0xe0, /* ######...... */ + 0x90, /* ##....##.... */ + 0x48, /* ..##....##.. */ + 0x38, /* ....######.. */ + 0x08, /* ........##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + /* 0x87 ('.') */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x88 ('.') */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0xa8, /* ##..##..##.. */ + 0xc8, /* ####....##.. */ + 0xa8, /* ##..##..##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x89 ('.') */ + 0x00, /* ............ */ + 0x38, /* ....######.. */ + 0x48, /* ..##....##.. */ + 0x58, /* ..##..####.. */ + 0x28, /* ....##..##.. */ + 0x00, /* ............ */ + 0x78, /* ..########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x8a ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x24, /* ....##....## */ + 0x48, /* ..##....##.. */ + 0x90, /* ##....##.... */ + 0x48, /* ..##....##.. */ + 0x24, /* ....##....## */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x8b ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x78, /* ..########.. */ + 0x08, /* ........##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x8c ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x78, /* ..########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x8d ('.') */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0xe8, /* ######..##.. */ + 0xc8, /* ####....##.. */ + 0xc8, /* ####....##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x8e ('.') */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x8f ('.') */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x90 ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0xf8, /* ##########.. */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x91 ('.') */ + 0x30, /* ....####.... */ + 0x48, /* ..##....##.. */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x78, /* ..########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x92 ('.') */ + 0x70, /* ..######.... */ + 0x08, /* ........##.. */ + 0x30, /* ....####.... */ + 0x08, /* ........##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x93 ('.') */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x94 ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xc8, /* ####....##.. */ + 0xb0, /* ##..####.... */ + 0x80, /* ##.......... */ + 0x00, /* ............ */ + /* 0x95 ('.') */ + 0x00, /* ............ */ + 0x78, /* ..########.. */ + 0xe8, /* ######..##.. */ + 0xe8, /* ######..##.. */ + 0x68, /* ..####..##.. */ + 0x28, /* ....##..##.. */ + 0x28, /* ....##..##.. */ + 0x28, /* ....##..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x96 ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x97 ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + /* 0x98 ('.') */ + 0x20, /* ....##...... */ + 0x60, /* ..####...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x99 ('.') */ + 0x00, /* ............ */ + 0x30, /* ....####.... */ + 0x48, /* ..##....##.. */ + 0x48, /* ..##....##.. */ + 0x30, /* ....####.... */ + 0x00, /* ............ */ + 0x78, /* ..########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x9a ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x90, /* ##....##.... */ + 0x48, /* ..##....##.. */ + 0x24, /* ....##....## */ + 0x48, /* ..##....##.. */ + 0x90, /* ##....##.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x9b ('.') */ + 0x40, /* ..##........ */ + 0xc0, /* ####........ */ + 0x40, /* ..##........ */ + 0x40, /* ..##........ */ + 0xe4, /* ######....## */ + 0x0c, /* ........#### */ + 0x14, /* ......##..## */ + 0x3c, /* ....######## */ + 0x04, /* ..........## */ + 0x00, /* ............ */ + /* 0x9c ('.') */ + 0x40, /* ..##........ */ + 0xc0, /* ####........ */ + 0x40, /* ..##........ */ + 0x40, /* ..##........ */ + 0xe8, /* ######..##.. */ + 0x14, /* ......##..## */ + 0x04, /* ..........## */ + 0x08, /* ........##.. */ + 0x1c, /* ......###### */ + 0x00, /* ............ */ + /* 0x9d ('.') */ + 0xc0, /* ####........ */ + 0x20, /* ....##...... */ + 0x40, /* ..##........ */ + 0x20, /* ....##...... */ + 0xc8, /* ####....##.. */ + 0x18, /* ......####.. */ + 0x28, /* ....##..##.. */ + 0x78, /* ..########.. */ + 0x08, /* ........##.. */ + 0x00, /* ............ */ + /* 0x9e ('.') */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x40, /* ..##........ */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0x9f ('.') */ + 0x40, /* ..##........ */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xa0 ('.') */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xa1 ('.') */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xa2 ('.') */ + 0x48, /* ..##....##.. */ + 0xb0, /* ##..####.... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xa3 ('.') */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xa4 ('.') */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xa5 ('.') */ + 0x00, /* ............ */ + 0x3c, /* ....######## */ + 0x50, /* ..##..##.... */ + 0x90, /* ##....##.... */ + 0x9c, /* ##....###### */ + 0xf0, /* ########.... */ + 0x90, /* ##....##.... */ + 0x9c, /* ##....###### */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xa6 ('.') */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x20, /* ....##...... */ + 0x40, /* ..##........ */ + /* 0xa7 ('.') */ + 0x40, /* ..##........ */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xf0, /* ########.... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xa8 ('.') */ + 0x10, /* ......##.... */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xf0, /* ########.... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xa9 ('.') */ + 0x20, /* ....##...... */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xf0, /* ########.... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xaa ('.') */ + 0x50, /* ..##..##.... */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xf0, /* ########.... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xab ('.') */ + 0x40, /* ..##........ */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xac ('.') */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xad ('.') */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x70, /* ..######.... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xae ('.') */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xaf ('.') */ + 0x00, /* ............ */ + 0xf0, /* ########.... */ + 0x48, /* ..##....##.. */ + 0x48, /* ..##....##.. */ + 0xe8, /* ######..##.. */ + 0x48, /* ..##....##.. */ + 0x48, /* ..##....##.. */ + 0xf0, /* ########.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xb0 ('.') */ + 0x28, /* ....##..##.. */ + 0x50, /* ..##..##.... */ + 0x88, /* ##......##.. */ + 0xc8, /* ####....##.. */ + 0xa8, /* ##..##..##.. */ + 0x98, /* ##....####.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xb1 ('.') */ + 0x40, /* ..##........ */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xb2 ('.') */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xb3 ('.') */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xb4 ('.') */ + 0x28, /* ....##..##.. */ + 0x50, /* ..##..##.... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xb5 ('.') */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xb6 ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xb7 ('.') */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x98, /* ##....####.. */ + 0x98, /* ##....####.. */ + 0xa8, /* ##..##..##.. */ + 0xc8, /* ####....##.. */ + 0xc8, /* ####....##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xb8 ('.') */ + 0x40, /* ..##........ */ + 0x20, /* ....##...... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xb9 ('.') */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xba ('.') */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xbb ('.') */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xbc ('.') */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xbd ('.') */ + 0x00, /* ............ */ + 0x80, /* ##.......... */ + 0xf0, /* ########.... */ + 0x88, /* ##......##.. */ + 0xf0, /* ########.... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xbe ('.') */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x90, /* ##....##.... */ + 0xa0, /* ##..##...... */ + 0x90, /* ##....##.... */ + 0x88, /* ##......##.. */ + 0xb0, /* ##..####.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xbf ('.') */ + 0x40, /* ..##........ */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x08, /* ........##.. */ + 0x78, /* ..########.. */ + 0x88, /* ##......##.. */ + 0x78, /* ..########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xc0 ('.') */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x08, /* ........##.. */ + 0x78, /* ..########.. */ + 0x88, /* ##......##.. */ + 0x78, /* ..########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xc1 ('.') */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x08, /* ........##.. */ + 0x78, /* ..########.. */ + 0x88, /* ##......##.. */ + 0x78, /* ..########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xc2 ('.') */ + 0x28, /* ....##..##.. */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x08, /* ........##.. */ + 0x78, /* ..########.. */ + 0x88, /* ##......##.. */ + 0x78, /* ..########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xc3 ('.') */ + 0x00, /* ............ */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x08, /* ........##.. */ + 0x78, /* ..########.. */ + 0x88, /* ##......##.. */ + 0x78, /* ..########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xc4 ('.') */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0x08, /* ........##.. */ + 0x78, /* ..########.. */ + 0x88, /* ##......##.. */ + 0x78, /* ..########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xc5 ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x78, /* ..########.. */ + 0x14, /* ......##..## */ + 0x7c, /* ..########## */ + 0x90, /* ##....##.... */ + 0x7c, /* ..########## */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xc6 ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x80, /* ##.......... */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x20, /* ....##...... */ + 0x40, /* ..##........ */ + /* 0xc7 ('.') */ + 0x40, /* ..##........ */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xc8 ('.') */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xc9 ('.') */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xca ('.') */ + 0x00, /* ............ */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xcb ('.') */ + 0x40, /* ..##........ */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x60, /* ..####...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xcc ('.') */ + 0x20, /* ....##...... */ + 0x40, /* ..##........ */ + 0x00, /* ............ */ + 0x60, /* ..####...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xcd ('.') */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x60, /* ..####...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xce ('.') */ + 0x00, /* ............ */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x60, /* ..####...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xcf ('.') */ + 0x00, /* ............ */ + 0xc0, /* ####........ */ + 0x30, /* ....####.... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xd0 ('.') */ + 0x28, /* ....##..##.. */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0xb0, /* ##..####.... */ + 0xc8, /* ####....##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xd1 ('.') */ + 0x40, /* ..##........ */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xd2 ('.') */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xd3 ('.') */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xd4 ('.') */ + 0x28, /* ....##..##.. */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xd5 ('.') */ + 0x00, /* ............ */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xd6 ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xd7 ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x78, /* ..########.. */ + 0x98, /* ##....####.. */ + 0xa8, /* ##..##..##.. */ + 0xc8, /* ####....##.. */ + 0xf0, /* ########.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xd8 ('.') */ + 0x40, /* ..##........ */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x98, /* ##....####.. */ + 0x68, /* ..####..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xd9 ('.') */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x98, /* ##....####.. */ + 0x68, /* ..####..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xda ('.') */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x98, /* ##....####.. */ + 0x68, /* ..####..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xdb ('.') */ + 0x00, /* ............ */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x98, /* ##....####.. */ + 0x68, /* ..####..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xdc ('.') */ + 0x00, /* ............ */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x98, /* ##....####.. */ + 0x68, /* ..####..##.. */ + 0x08, /* ........##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + /* 0xdd ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x80, /* ##.......... */ + 0xf0, /* ########.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xf0, /* ########.... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + /* 0xde ('.') */ + 0x00, /* ............ */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x98, /* ##....####.. */ + 0x68, /* ..####..##.. */ + 0x08, /* ........##.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + /* 0xdf ('.') */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xe0 ('.') */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x08, /* ........##.. */ + 0x78, /* ..########.. */ + 0x88, /* ##......##.. */ + 0x78, /* ..########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xe1 ('.') */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xe2 ('.') */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x08, /* ........##.. */ + 0x78, /* ..########.. */ + 0x88, /* ##......##.. */ + 0x78, /* ..########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xe3 ('.') */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x10, /* ......##.... */ + 0x18, /* ......####.. */ + /* 0xe4 ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x08, /* ........##.. */ + 0x78, /* ..########.. */ + 0x88, /* ##......##.. */ + 0x78, /* ..########.. */ + 0x10, /* ......##.... */ + 0x18, /* ......####.. */ + /* 0xe5 ('.') */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xe6 ('.') */ + 0x10, /* ......##.... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x80, /* ##.......... */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xe7 ('.') */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xe8 ('.') */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x80, /* ##.......... */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xe9 ('.') */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xea ('.') */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x80, /* ##.......... */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xeb ('.') */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xec ('.') */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x80, /* ##.......... */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xed ('.') */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0xf0, /* ########.... */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0x88, /* ##......##.. */ + 0xf0, /* ########.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xee ('.') */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0x08, /* ........##.. */ + 0x68, /* ..####..##.. */ + 0x98, /* ##....####.. */ + 0x88, /* ##......##.. */ + 0x98, /* ##....####.. */ + 0x68, /* ..####..##.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xef ('.') */ + 0x00, /* ............ */ + 0x78, /* ..########.. */ + 0x44, /* ..##......## */ + 0x44, /* ..##......## */ + 0xe4, /* ######....## */ + 0x44, /* ..##......## */ + 0x44, /* ..##......## */ + 0x78, /* ..########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xf0 ('.') */ + 0x00, /* ............ */ + 0x10, /* ......##.... */ + 0x38, /* ....######.. */ + 0x10, /* ......##.... */ + 0x70, /* ..######.... */ + 0x90, /* ##....##.... */ + 0x90, /* ##....##.... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xf1 ('.') */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0xf0, /* ########.... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xf2 ('.') */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xf3 ('.') */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0xf0, /* ########.... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xf4 ('.') */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xf5 ('.') */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0xf0, /* ########.... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xf6 ('.') */ + 0x00, /* ............ */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xf7 ('.') */ + 0x00, /* ............ */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xf0, /* ########.... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xf8, /* ##########.. */ + 0x20, /* ....##...... */ + 0x30, /* ....####.... */ + /* 0xf8 ('.') */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0x70, /* ..######.... */ + 0x20, /* ....##...... */ + 0x30, /* ....####.... */ + /* 0xf9 ('.') */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0xf0, /* ########.... */ + 0x80, /* ##.......... */ + 0x80, /* ##.......... */ + 0xf8, /* ##########.. */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xfa ('.') */ + 0x50, /* ..##..##.... */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0xf8, /* ##########.. */ + 0x80, /* ##.......... */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xfb ('.') */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x80, /* ##.......... */ + 0x98, /* ##....####.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xfc ('.') */ + 0x20, /* ....##...... */ + 0x50, /* ..##..##.... */ + 0x00, /* ............ */ + 0x68, /* ..####..##.. */ + 0x90, /* ##....##.... */ + 0x60, /* ..####...... */ + 0x80, /* ##.......... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + /* 0xfd ('.') */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x80, /* ##.......... */ + 0x98, /* ##....####.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00, /* ............ */ + /* 0xfe ('.') */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x68, /* ..####..##.. */ + 0x90, /* ##....##.... */ + 0x60, /* ..####...... */ + 0x80, /* ##.......... */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + /* 0xff ('.') */ + 0x20, /* ....##...... */ + 0x00, /* ............ */ + 0x70, /* ..######.... */ + 0x88, /* ##......##.. */ + 0x80, /* ##.......... */ + 0x98, /* ##....####.. */ + 0x88, /* ##......##.. */ + 0x70, /* ..######.... */ + 0x00, /* ............ */ + 0x00 /* ............ */ +}; + +gfx_bitmap_font_t gfxfont_6x10 = { + -1, /* resource ID */ + 256, /* # of characters */ + gfxfont_6x10_widths, /* Widths */ + 1, /* Bytes per row */ + 11, /* Line height */ + 10, /* Char height */ + 10, /* Char size (occupied, in bytes) */ + gfxfont_6x10_data /* Bulk data */ +}; diff --git a/engines/sci/gfx/font.c b/engines/sci/gfx/font.c new file mode 100644 index 0000000000..c5de8715b8 --- /dev/null +++ b/engines/sci/gfx/font.c @@ -0,0 +1,385 @@ +/*************************************************************************** + font.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + + +#include +#include +#include + +int font_counter = 0; + +void +gfxr_free_font(gfx_bitmap_font_t *font) +{ + if (font->widths) + free(font->widths); + + if (font->data) + free(font->data); + + --font_counter; + + free(font); +} + + + +void +scale_char(byte *dest, byte *src, int width, int height, int newwidth, int xfact, int yfact) +{ + int x, y; + + for (y = 0; y < height; y++) { + int yc; + byte *bdest = dest; + byte *bsrc = src; + + for (x = 0; x < width; x++) { + int xbitc; + int bits = 0; + int value = 0; + + for (xbitc = 128; xbitc; xbitc >>= 1) { + int xc; + + for (xc = 0; xc < xfact; xc++) { + if (*bsrc & xbitc) + value |= 1; + value <<= 1; + + if (++bits == 8) { + *bdest++ = value; + bits = value = 0; + } + } + } + bsrc++; + } + + src += width; + for (yc = 1; yc < yfact; yc++) { + memcpy(dest + newwidth, dest, newwidth); + dest += newwidth; + } + dest += newwidth; + } +} + +gfx_bitmap_font_t * +gfxr_scale_font_unfiltered(gfx_bitmap_font_t *orig_font, gfx_mode_t *mode) +{ + gfx_bitmap_font_t *font = (gfx_bitmap_font_t*)sci_malloc(sizeof(gfx_bitmap_font_t)); + int height = orig_font->height * mode->yfact; + int width = 0; + int byte_width; + int i; + + font->chars_nr = orig_font->chars_nr; + for (i = 0; i < font->chars_nr; i++) + if (orig_font->widths[i] > width) + width = orig_font->widths[i]; + + width *= mode->xfact; + byte_width = (width + 7) >> 3; + if (byte_width == 3) + byte_width = 4; + if (byte_width > 4) + byte_width = (byte_width + 3) & ~3; + + font->row_size = byte_width; + font->height = height; + font->line_height = orig_font->line_height * mode->yfact; + + font->widths = (int*)sci_malloc(sizeof(int) * orig_font->chars_nr); + font->char_size = byte_width * height; + font->data = (byte*)sci_malloc(font->chars_nr * font->char_size); + + for (i = 0; i < font->chars_nr; i++) { + font->widths[i] = orig_font->widths[i] * mode->xfact; + scale_char(font->data + font->char_size * i, + orig_font->data + orig_font->char_size * i, + orig_font->row_size, orig_font->height, + font->row_size, + mode->xfact, mode->yfact); + } + return font; +} + + +gfx_bitmap_font_t * +gfxr_scale_font(gfx_bitmap_font_t *orig_font, gfx_mode_t *mode, gfxr_font_scale_filter_t filter) +{ + GFXWARN("This function hasn't been tested yet!\n"); + + switch (filter) { + + case GFXR_FONT_SCALE_FILTER_NONE: + return gfxr_scale_font_unfiltered(orig_font, mode); + + default: + GFXERROR("Invalid font filter mode %d!\n", filter); + return NULL; + } +} + + + + +text_fragment_t * +gfxr_font_calculate_size(gfx_bitmap_font_t *font, int max_width, const char *text, + int *width, int *height, + int *lines, int *line_height_p, int *last_offset_p, + int flags) +{ + int est_char_width = font->widths[(font->chars_nr > 'M')? 'M' : font->chars_nr - 1]; + /* 'M' is typically among the widest chars */ + int fragments_nr; + text_fragment_t *fragments; + int lineheight = font->line_height; + int maxheight = lineheight; + int last_breakpoint = 0; + int last_break_width = 0; + int max_allowed_width = max_width; + int maxwidth = 0, localmaxwidth = 0; + int current_fragment = 1; + const char *breakpoint_ptr = NULL; + unsigned char foo; + + if (line_height_p) + *line_height_p = lineheight; + + if (max_width>1) fragments_nr = 3 + (strlen(text) * est_char_width)*3 / (max_width << 1); else fragments_nr = 1; + + fragments = (text_fragment_t*)sci_calloc(sizeof(text_fragment_t), fragments_nr); + + + fragments[0].offset = text; + + while ((foo = *text++)) { + + if (foo >= font->chars_nr) { + GFXWARN("Invalid char 0x%02x (max. 0x%02x) encountered in text string '%s', font %04x\n", + foo, font->chars_nr, text, font->ID); + if (font->chars_nr > ' ') + foo = ' '; + else { + free(fragments); + return NULL; + } + } + + if (((foo == '\n') || (foo == 0x0d)) + && !(flags & GFXR_FONT_FLAG_NO_NEWLINES)) { + + fragments[current_fragment-1].length = text - 1 - fragments[current_fragment-1].offset; + + if (*text) + maxheight += lineheight; + + if (foo == 0x0d && *text == '\n') + text++; /* Interpret DOS-style CR LF as single NL */ + + fragments[current_fragment++].offset = text; + + if (localmaxwidth > maxwidth) + maxwidth = localmaxwidth; + + if (current_fragment == fragments_nr) + fragments = (text_fragment_t*)sci_realloc(fragments, sizeof(text_fragment_t) * (fragments_nr <<= 1)); + + localmaxwidth = 0; + + } else { /* foo != '\n' */ + localmaxwidth += font->widths[foo]; + + if (localmaxwidth > max_allowed_width) { + int blank_break = 1; /* break is at a blank char, i.e. not within a word */ + + maxheight += lineheight; + + if (last_breakpoint == 0) { /* Text block too long and without whitespace? */ + last_breakpoint = localmaxwidth - font->widths[foo]; + last_break_width = 0; + --text; + blank_break = 0; /* non-blank break */ + } else { + text = breakpoint_ptr + 1; + assert(breakpoint_ptr); + } + + if (last_breakpoint == 0) { + GFXWARN("Warning: maxsize %d too small for '%s'\n", + max_allowed_width, text); + } + + if (last_breakpoint > maxwidth) + maxwidth = last_breakpoint; + + fragments[current_fragment-1].length = text - blank_break - fragments[current_fragment-1].offset; + fragments[current_fragment++].offset = text; + + if (current_fragment == fragments_nr) + fragments = (text_fragment_t*)sci_realloc(fragments, sizeof(text_fragment_t *) * (fragments_nr <<= 1)); + + localmaxwidth = localmaxwidth - last_breakpoint; + if (!(flags & GFXR_FONT_FLAG_COUNT_WHITESPACE)) + localmaxwidth -= last_break_width; + last_breakpoint = localmaxwidth = 0; + + } else if (*text == ' ') { + last_breakpoint = localmaxwidth; + last_break_width = font->widths[foo]; + breakpoint_ptr = text; + } + + } + } + + if (localmaxwidth > maxwidth) + *width = localmaxwidth; + else + *width = maxwidth; + + if (last_offset_p) + *last_offset_p = localmaxwidth; + + if (height) + *height = maxheight; + if (lines) + *lines = current_fragment; + + fragments[current_fragment-1].length = text - fragments[current_fragment-1].offset - 1; + + return fragments; +} + + +static inline void +render_char(byte *dest, byte *src, int width, int line_width, int lines, int bytes_per_src_line, int fg0, int fg1, int bg) +{ + int x, y; + + for (y = 0; y < lines; y++) { + int dat = 0; + byte *vdest = dest; + byte *vsrc = src; + int xc = 0; + + for (x = 0; x < width; x++) { + if (!xc) { + dat = *vsrc++; + xc = 8; + } + xc--; + + if (dat & 0x80) + *vdest++ = ((xc ^ y) & 1)? fg0 : fg1; /* dither */ + else + *vdest++ = bg; + + dat <<= 1; + } + src += bytes_per_src_line; + dest += line_width; + } +} + +gfx_pixmap_t * +gfxr_draw_font(gfx_bitmap_font_t *font, const char *stext, int characters, + gfx_pixmap_color_t *fg0, gfx_pixmap_color_t *fg1, gfx_pixmap_color_t *bg) +{ + unsigned char *text = (unsigned char *) stext; + int height = font->height; + int width = 0; + gfx_pixmap_t *pxm; + int fore_0, fore_1, back; + int i; + int hack = 0; + gfx_pixmap_color_t dummy = {0}; + byte *offset; + + for (i = 0; i < characters; i++) { + int ch = (int) text[i]; + + if (ch >= font->chars_nr) { + GFXERROR("Invalid character 0x%02x encountered!\n", text[i]); + return NULL; + } + + width += font->widths[ch]; + } + + pxm = gfx_pixmap_alloc_index_data(gfx_new_pixmap(width, height, GFX_RESID_NONE, 0, 0)); + + pxm->colors_nr = !!fg0 + !!fg1 + !!bg; + if (pxm->colors_nr == 0) + { + GFXWARN("Pixmap would have zero colors, resetting!\n"); + pxm->colors_nr = 3; + hack = 1; + fg0 = fg1 = bg = &dummy; + } + pxm->colors = (gfx_pixmap_color_t*)sci_malloc(sizeof(gfx_pixmap_color_t) * pxm->colors_nr); +#ifdef SATISFY_PURIFY + memset(pxm->colors, 0, sizeof(gfx_pixmap_color_t) * pxm->colors_nr); +#endif + pxm->flags |= GFX_PIXMAP_FLAG_PALETTE_ALLOCATED | GFX_PIXMAP_FLAG_DONT_UNALLOCATE_PALETTE; + + i = 0; + + if (fg0 || hack) { + memcpy(pxm->colors + i, fg0, sizeof(gfx_pixmap_color_t)); + fore_0 = i++; + } else fore_0 = pxm->color_key; + + if (fg1 || hack) { + memcpy(pxm->colors + i, fg1, sizeof(gfx_pixmap_color_t)); + fore_1 = i++; + } else fore_1 = pxm->color_key; + + if (bg || hack) { + memcpy(pxm->colors + i, bg, sizeof(gfx_pixmap_color_t)); + back = i++; + } else back = pxm->color_key; + + offset = pxm->index_data; + + memset(pxm->index_data, back, pxm->index_xl * pxm->index_yl); + for (i = 0; i < characters; i++) { + unsigned char ch = text[i]; + width = font->widths[ch]; + + render_char(offset, font->data + (ch * font->char_size), width, + pxm->index_xl, pxm->index_yl, font->row_size, + fore_0, fore_1, back); + + offset += width; + } + + return pxm; +} + diff --git a/engines/sci/gfx/gfx_console.c b/engines/sci/gfx/gfx_console.c new file mode 100644 index 0000000000..0cde8092f3 --- /dev/null +++ b/engines/sci/gfx/gfx_console.c @@ -0,0 +1,1450 @@ +/*************************************************************************** + gfx_console.c Copyright (C) 2002 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Graphical on-screen console */ + + +#include +#include + +#ifdef WANT_CONSOLE +# define CON_MAX_CLUSTERS 16 + +#define CON_CLUSTER_SIZE 64 + +/* Number of console entries stored = CON_MAX_CLUSTERS * CON_CLUSTER_SIZE */ + +#define CON_ENTRY_USED(e) ((e).height > 0) + +#define CON_INPUT_HISTORY_SIZE 64 +#define CON_BUILTIN_CHARS_NR 256 +#define CON_BUILTIN_CHARS_HEIGHT 8 +#define CON_BUILTIN_CHARS_WIDTH 8 + +#define CON_OVERWRAP_SYMBOL 0x10 + +#define CON_GFX_PROMPT "$ " + +extern byte con_builtin_font_data[]; + +typedef struct { + int height; /* Number of pixels occupied by this entry, or 0 if unused */ + int pixmaps_nr; + gfx_pixmap_t **pixmaps; + char *text; /* Dynamically allocated */ +} con_entry_t; + + +typedef struct _con_buffer { + con_entry_t entries[CON_CLUSTER_SIZE]; + struct _con_buffer *next; + struct _con_buffer *prev; +} con_buffer_t; + +typedef struct { + con_buffer_t *buf; + int entry; + int offset; /* pixel offset relative to the bottom */ +} con_pos_t; + +typedef struct { + int locked_to_end; + con_pos_t pos; + gfx_pixmap_t *background; + gfx_color_t color_bg, color_transparent, color_cursor, color_text, color_input; + gfx_pixmap_t *input_prompt; + gfx_pixmap_t *input_precursor; + gfx_pixmap_t *input_oncursor; + gfx_pixmap_t *input_postcursor; + int cursor_position; + int input_window; /* First character to display, may be up to -2 to include prompt */ + char *input_text; + int input_bufsize; + int input_prompt_pos; /* -strlen(input prompt) */ + int input_history_pos; + int partial_write; + char *input_history[CON_INPUT_HISTORY_SIZE]; +} con_t; + + +/* Visual options for the con */ +static int con_bg_red = 0; +static int con_bg_green = 0; +static int con_bg_blue = 64; +static int con_bg_alpha = 64; +static int con_displayed_lines = 180; +static int con_border_width = 3; + +static int con_top_buffer_entry_nr = 0; +static int con_buffer_clusters = 0; +static con_buffer_t *con_buffer = NULL; +static con_buffer_t *con_buffer_tail = NULL; +static con_t con; /* The global con */ +static gfx_bitmap_font_t con_font; +static int con_font_initialized = 0; +static gfx_state_t *con_last_gfx_state = NULL; + +/*-- Forwards --*/ +static void +_con_free_entry_pixmaps(gfx_state_t *state, con_entry_t *entry); +/* Free all pixmaps from the specified entry +** Parameters: (gfx_state_t *) state: The state to free from +** (con_entry_t *) entry: The entry to liberate from its pixmaps +*/ + +static gfx_pixmap_t * +_con_render_text(gfx_state_t *state, char *text, int len, + gfx_color_t *color, gfx_color_t *bgcolor); +/* Renders the specified text in the specified fg color +** Parameters: (gfx_state_t *) state: The state to render in +** (char *) text: The text to render +** (int) len: Length of the text to render, or -1 for strlen +** (gfx_color_t *) color: The fg color to choose +** (gfx_color_t *) bgcolor: The bg color to choose +** Returns : (gfx_pixmap_t *) An appropriate pixmap +*/ + + +void +con_jump_to_end(gfx_state_t *gfx); +/* Makes the console jump to the logical end of its buffer and redraws +** Parameters: (gfx_stat_t *) gfx: The graphics state to use fo rdrawing +*/ + +static void +_con_redraw(gfx_state_t *state, int update_display_field, int update_input_field); +/* Performs a (partial) redraw +** Parameters: (gfx_state_t *) state: The graphical state to draw with +** (int) update_display_field: Whether the upper part of the +** console, used to display test, should be updated +** (int) update_input_field: Whether the lower part of the +** console, used to display the user input, should be +** updated +*/ + +static void +free_con_buffer(con_buffer_t *buf); +/* Frees the specified con buffer and all of its predecessors +** Parameters: (con_buffer_t *) buf: The buffer to free +** Pixmaps are freed if neccessary, using the last used gfx state for con_gfx_show(). +*/ + +void +con_gfx_hide(gfx_state_t *state); +/* Removes the console, restoring the background graphics +** Parameters: (gfx_state_t *state: The graphics state to draw with +*/ + +static gfx_pixmap_t ** +_con_render_text_multiline(gfx_state_t *state, char *text, int maxchars, int *nr); +/*-- code --*/ + +static rect_t +con_box() +{ + return gfx_rect(0, 0, 320, + con_displayed_lines + + con_border_width); +} + +void +con_gfx_show(gfx_state_t *state) +{ + gfxop_set_clip_zone(state, gfx_rect(0, 0, 320, 200)); + con_last_gfx_state = state; + + con.locked_to_end = 1; + con.background = gfxop_grab_pixmap(state, con_box()); + + gfxop_set_color(state, &con.color_bg, con_bg_red, con_bg_green, + con_bg_blue, con_bg_alpha, -1, -1); + gfxop_set_color(state, &con.color_transparent, 0, 0, 0, 255, -1, -1); + gfxop_set_color(state, &con.color_text, 255, 255, 255, 0, -1, -1); + gfxop_set_color(state, &con.color_input, 255, 255, 0, 0, -1, -1); + gfxop_set_color(state, &con.color_cursor, 255, 0, 0, 0, -1, -1); + + if (!con.input_prompt) { + con.input_prompt = _con_render_text(state, CON_GFX_PROMPT, -1, + &con.color_input, NULL); + con.input_text = (char*)sci_malloc(con.input_bufsize = 64); + con.input_text[0] = 0; + con.input_history_pos = 0; + con.partial_write = 0; + memset(con.input_history, 0, sizeof(char *) * CON_INPUT_HISTORY_SIZE); + } + + con_jump_to_end(state); + _con_redraw(state, 0, 1); +} + +static void +_con_draw_bg_pic(gfx_state_t *state, rect_t zone) +{ + gfxop_draw_pixmap(state, con.background, zone, gfx_point(zone.x,zone.y)); +} + +void +con_jump_to_end(gfx_state_t *state) +{ + con.locked_to_end = 1; + _con_redraw(state, 1, 0); +} + + +void +con_fold_text(gfx_state_t *state) +{ + con_buffer_t *seeker = con_buffer; + + /* Fold all text pixmaps */ + while (seeker) { + int i; + for (i = 0; i < CON_CLUSTER_SIZE; i++) + if (CON_ENTRY_USED(seeker->entries[i]) + && seeker->entries[i].text && seeker->entries[i].pixmaps_nr) + _con_free_entry_pixmaps(state, &seeker->entries[i]); + + seeker = seeker->next; + } +} + +void +con_gfx_hide(gfx_state_t *state) +{ + /* Restore background */ + _con_draw_bg_pic(state, con_box()); + + if (con.background) + gfxop_free_pixmap(state, con.background); + con.background = NULL; + + con_fold_text(state); + gfxop_update(state); +} + +static inline con_buffer_t * +_create_con_buffer(con_buffer_t *prev) +{ + con_buffer_t *buf = (con_buffer_t *)sci_malloc(sizeof (con_buffer_t)); + int i; + + for (i = 0; i < CON_CLUSTER_SIZE; i++) + buf->entries[i].height = 0; + + buf->prev = prev; + buf->next = NULL; + if (prev) + prev->next = buf; + + con_buffer_clusters++; + + return buf; +} + + +static inline void +_add_into_con_buffer(gfx_pixmap_t *pixmap, char *text) +{ + con_entry_t *target; + + if (!con_buffer) { + con_buffer = con_buffer_tail = _create_con_buffer(NULL); + con_top_buffer_entry_nr = 0; + } + + if (con_top_buffer_entry_nr == CON_CLUSTER_SIZE) { + /* Out of entries in this cluster */ + con_buffer = _create_con_buffer(con_buffer); + con_top_buffer_entry_nr = 0; + } + + target = con_buffer->entries + con_top_buffer_entry_nr; + + if (con.partial_write && text) { + int real_entry = con_top_buffer_entry_nr - 1; + int needlen = strlen(text); + char *oldtext; + + if (real_entry < 0) + target = con_buffer->prev->entries + CON_CLUSTER_SIZE - 1; + else + target = con_buffer->entries + real_entry; + + if (target->pixmaps) + _con_free_entry_pixmaps(con_last_gfx_state, target); + + needlen += strlen(target->text); + oldtext = target->text; + target->text = (char *)sci_malloc(needlen+1); + strcpy(target->text, oldtext); + strcat(target->text, text); + free(oldtext); + free(text); + + con.partial_write = (target->text && *(target->text) && + target->text[strlen(target->text) - 1] != '\n'); + + return; + } + else ++con_top_buffer_entry_nr; + + + con.partial_write = (text && *(text) && + text[strlen(text) - 1] != '\n'); + + if (pixmap) + target->height = pixmap->index_yl; + else + target->height = 1; /* Will be calculated on demand */ + + if (!pixmap) { + target->pixmaps = NULL; + target->pixmaps_nr = 0; + } else { + target->pixmaps = (gfx_pixmap_t **)sci_malloc(sizeof(gfx_pixmap_t *)); + target->pixmaps[0] = pixmap; + target->pixmaps_nr = 1; + } + target->text = text; + + while (con_buffer_clusters > CON_MAX_CLUSTERS + && con_buffer_tail) { + if (con_buffer_tail->next) { + con_buffer_tail = con_buffer_tail->next; + free_con_buffer(con_buffer_tail->prev); + } else { + fprintf(stderr,"WARNING: During cleanup, con_buffer_tail ran out!\n"); + free_con_buffer(con_buffer_tail->prev); + con_buffer_tail = con_buffer = NULL; + } + } +} + + +void +con_gfx_insert_string(char *string) +{ + if (string) + _add_into_con_buffer(NULL, string); +} + +void +con_gfx_insert_pixmap(gfx_pixmap_t *pixmap) +{ + if (pixmap) + _add_into_con_buffer(pixmap, NULL); +} + +static int +_unfold_graphics(gfx_state_t *state, con_entry_t *drawme, int nr_chars) + /* Returns whether unfolding was neccessary */ +{ + if (drawme->text && !drawme->pixmaps_nr) { + int i; + drawme->pixmaps = _con_render_text_multiline(state, drawme->text, + nr_chars, + &drawme->pixmaps_nr); + + drawme->height = 0; + for (i = 0; i < drawme->pixmaps_nr; i++) + drawme->height += + (drawme->pixmaps[i]->yl + state->driver->mode->yfact - 1) + / state->driver->mode->yfact; + /* Divide by scaler, round up */ + return 1; + } + + return 0; +} + +void +con_scroll(gfx_state_t *state, int offset, int maxchars) +{ + con_entry_t *next_entry; + /* Scrolls within the display by the specified amount */ + + if (con.locked_to_end) { + con.pos.buf = con_buffer; + con.pos.offset = 0; + con.pos.entry = con_top_buffer_entry_nr - 1; + } + + if (!con.pos.buf) + return; + + con.locked_to_end = 0; + + con.pos.offset += offset; /* offset exceeds size -> Use PREVIOUS entry */ + + while (con.pos.offset < 0 || con.pos.offset > con.pos.buf->entries[con.pos.entry].height) { + + if (con.pos.offset < 0) { + if (++con.pos.entry == CON_CLUSTER_SIZE + || ((con.pos.buf == con_buffer) + && (con.pos.entry >= con_top_buffer_entry_nr))) { + if (con.pos.buf->next) { + con.pos.entry = 0; + con.pos.buf = con.pos.buf->next; + } else { + con_jump_to_end(state); + return; + } + } + + next_entry = con.pos.buf->entries + con.pos.entry; + + _unfold_graphics(state, next_entry, maxchars); + con.pos.offset += next_entry->height; + } else { /* offset too great ? */ + + if (con.pos.entry == 0) { + if (con.pos.buf->prev) { + con.pos.entry = CON_CLUSTER_SIZE; + con.pos.buf = con.pos.buf->prev; + } else { + con.pos.offset = con.pos.buf->entries[0].height - 1; + return; + } + } + --con.pos.entry; + + next_entry = con.pos.buf->entries + con.pos.entry; + + _unfold_graphics(state, next_entry, maxchars); + con.pos.offset -= next_entry->height; + + if (con.pos.offset < 0) + con.pos.offset = -con.pos.offset; + } + } +} + +void +con_gfx_init() +{ + con_set_string_callback(con_gfx_insert_string); + con_set_pixmap_callback(con_gfx_insert_pixmap); + con.input_prompt = NULL; + con.input_text = NULL; + con.input_window = con.input_prompt_pos = -(int)strlen(CON_GFX_PROMPT); + con.cursor_position = 0; + con.input_precursor = NULL; + con.input_postcursor = NULL; + con.input_oncursor = NULL; +} + + +static void +_init_con_font() +{ + int i; + + con_font.ID = 0; + con_font.chars_nr = CON_BUILTIN_CHARS_NR; + con_font.widths = (int *)sci_malloc(sizeof(int) * CON_BUILTIN_CHARS_NR); + for (i = 0; i < CON_BUILTIN_CHARS_NR; i++) + con_font.widths[i] = CON_BUILTIN_CHARS_WIDTH; + con_font.row_size = (CON_BUILTIN_CHARS_WIDTH + 7) >> 3; + con_font.height = con_font.line_height = CON_BUILTIN_CHARS_HEIGHT; + con_font.char_size = ((CON_BUILTIN_CHARS_WIDTH + 7) >> 3) * CON_BUILTIN_CHARS_HEIGHT; + con_font.data = con_builtin_font_data; + + con_font_initialized = 1; +} + +static gfx_pixmap_t ** +_con_render_text_multiline(gfx_state_t *state, char *text, int maxchars, int *nr) +{ + int pixmaps_allocd = 1; + gfx_pixmap_t **retval = (gfx_pixmap_t **)sci_malloc(sizeof(gfx_pixmap_t *) * pixmaps_allocd); + char *printbuf = (char *)sci_malloc(maxchars + 8); + int index = 0; + int overwrap = 0; + + while (*text) { + int len = 0; + + if (overwrap) { + len = 2; + printbuf[0] = ' '; + printbuf[1] = CON_OVERWRAP_SYMBOL; + overwrap = 0; + } + + while (*text && + *text != '\n' && len < maxchars) { + if (*text != '\t') + printbuf[len++] = *text; + else { + int tabwidth = 8 - (len & 7); + memset(printbuf + len, ' ', tabwidth); + len += tabwidth; + } + text++; + } + + if (*text && (*text != '\n')) + overwrap = 1; + + if (index == pixmaps_allocd) + retval = (gfx_pixmap_t **)sci_realloc(retval, sizeof(gfx_pixmap_t *) * (pixmaps_allocd+= 4)); + + retval[index++] = _con_render_text(state, printbuf, len, + &con.color_text, NULL); + if (*text) text += (1 - overwrap); + } + + *nr = index; + sci_free(printbuf); + return retval; +} + +static gfx_pixmap_t * +_con_render_text(gfx_state_t *state, char *text, int len, + gfx_color_t *color, gfx_color_t *bgcolor) +{ + gfx_pixmap_t *pxm; + + if (len < 0) + len = strlen(text); + + if (!con_font_initialized) + _init_con_font(); + + pxm = gfxr_draw_font(&con_font, text, len, + &color->visual, + &color->visual, + (bgcolor) ? &bgcolor->visual : NULL); + + pxm->flags |= GFX_PIXMAP_FLAG_SCALED_INDEX; + + gfx_xlate_pixmap(gfx_pixmap_alloc_data(pxm, state->driver->mode), + state->driver->mode, GFX_XLATE_FILTER_NONE); + return pxm; +} + +static inline int +_str_move_blank(char *data, int data_length, int initial_offset, int direction) + /* Finds the next beginning or end of a word */ +{ + int offset = initial_offset; + int abort = (direction < 0)? 0 : data_length; + int lookahead = (direction < 0)? -1 : 0; + + if (offset != abort) + offset += direction; + + while (offset != abort && isspace(data[offset])) + offset += direction; + + while (offset != abort && !isspace(data[offset + lookahead])) + offset += direction; + + return offset; +} + +char * +con_history_get_prev(int *handle) /* Should be -1 if not initialized yet */ +{ + int nexthandle; + + if (*handle == con.input_history_pos) + return NULL; + + if (*handle == -1) + *handle = con.input_history_pos; + + nexthandle = (*handle) - 1; + if (nexthandle == -1) + nexthandle = CON_INPUT_HISTORY_SIZE - 1; + + if (con.input_history[nexthandle]) + *handle = nexthandle; + + return con.input_history[nexthandle]; +} + +char * +con_history_get_next(int *handle) +{ + int last = (con.input_history_pos + CON_INPUT_HISTORY_SIZE - 1) % CON_INPUT_HISTORY_SIZE; + int nexthandle; + + if (*handle < 0 || *handle > CON_INPUT_HISTORY_SIZE) + return NULL; + + if (*handle == last) { + *handle = -1; + return NULL; /* End of history */ + } + + nexthandle = *handle + 1; + if (nexthandle == CON_INPUT_HISTORY_SIZE) + nexthandle = 0; + + if (con.input_history[nexthandle]) + *handle = nexthandle; + + return con.input_history[nexthandle]; +} + +void +con_history_archive(char *msg) +{ + char **writepos = &(con.input_history[con.input_history_pos]); + + if (*writepos) + sci_free(*writepos); + + *writepos = msg; + + if (++con.input_history_pos >= CON_INPUT_HISTORY_SIZE) + con.input_history_pos = 0; +} + +char * +con_gfx_read(gfx_state_t *state) +{ + int chwidth = CON_BUILTIN_CHARS_WIDTH / state->driver->mode->xfact; + int maxchars = 320 / chwidth; + sci_event_t evt; + char *retval; + int done = 0; + int slen = strlen(con.input_text); + int history_handle = -1; + + do { + int old_pos = con.cursor_position; + int must_resize = 0; /* Have to re-calculate the strlen */ + int must_redraw = 0; /* Redraw input field */ + int must_rewin = 0; /* Re-calculate window */ + int must_redraw_text = 0; /* Redraw display field */ + if (slen+1 >= con.input_bufsize) + con.input_text = (char *)sci_realloc(con.input_text, con.input_bufsize += 64); + + evt.type = 0; + while (!evt.type) + evt = gfxop_get_event(state, SCI_EVT_ANY); + + if (evt.type == SCI_EVT_KEYBOARD) { + + if (evt.buckybits & SCI_EVM_CTRL) { + switch(evt.data) { + case 'p': + case 'P': { + char *hist = con_history_get_prev(&history_handle); + + if (hist) { + sci_free(con.input_text); + con.input_text = sci_strdup(hist); + } + must_resize = must_redraw = must_rewin = 1; + } + break; + + case 'n': + case 'N': { + char *hist = con_history_get_next(&history_handle); + + if (hist) { + sci_free(con.input_text); + con.input_text = sci_strdup(hist); + } + must_resize = must_redraw = must_rewin = 1; + } + break; + + case 'a': + case 'A': /* C-a */ + con.cursor_position = 0; + break; + + case 'b': + case 'B': /* C-b */ + if (con.cursor_position) + --con.cursor_position; + break; + + case 'e': + case 'E': /* C-e */ + con.cursor_position = slen; + break; + + case 'f': + case 'F': /* C-f */ + if (con.cursor_position < slen) + ++con.cursor_position; + break; + + case 'd': + case 'D': + memmove(con.input_text + con.cursor_position, + con.input_text + con.cursor_position + 1, + slen - con.cursor_position); + must_resize = must_redraw = 1; + break; + + case 'h': + case 'H': + if (!con.cursor_position) + break; + + memmove(con.input_text + con.cursor_position - 1, + con.input_text + con.cursor_position, + slen - con.cursor_position + 1); + must_resize = must_redraw = 1; + --con.cursor_position; + break; + + case 'k': + case 'K': + con.input_text[con.cursor_position] = 0; + must_resize = must_redraw = 1; + break; + + case '`': return "go"; + + default: + break; + } + } else if (evt.buckybits & SCI_EVM_ALT) { + switch(evt.data) { + case 'b': + case 'B': + con.cursor_position = _str_move_blank(con.input_text, + slen, + con.cursor_position, + -1); + break; + + case 'f': + case 'F': + con.cursor_position = _str_move_blank(con.input_text, + slen, + con.cursor_position, + 1); + break; + + case 'd': + case 'D': { + int delpos = _str_move_blank(con.input_text, slen, + con.cursor_position, 1); + + must_resize = must_redraw = 1; + memmove(con.input_text + con.cursor_position, + con.input_text + delpos, + slen - delpos + 1); + } + + default: + break; + } + } else switch (evt.data) { + + case SCI_K_UP: { + char *hist = con_history_get_prev(&history_handle); + + if (hist) { + sci_free(con.input_text); + con.input_text = sci_strdup(hist); + } + must_resize = must_redraw = must_rewin = 1; + } + break; + + case SCI_K_DOWN: { + char *hist = con_history_get_next(&history_handle); + + if (hist) { + sci_free(con.input_text); + con.input_text = sci_strdup(hist); + } + must_resize = must_redraw = must_rewin = 1; + } + break; + + + case SCI_K_LEFT: + if (con.cursor_position) + --con.cursor_position; + break; + + case SCI_K_RIGHT: + if (con.cursor_position < slen) + ++con.cursor_position; + break; + + + case SCI_K_PGDOWN: + must_redraw_text = 1; + con_scroll(state, -75, maxchars); + break; + + case SCI_K_PGUP: + must_redraw_text = 1; + con_scroll(state, 75, maxchars); + break; + + case SCI_K_END: + con_jump_to_end(state); + must_redraw_text = 1; + break; + + case SCI_K_DELETE: + memmove(con.input_text + con.cursor_position, + con.input_text + con.cursor_position + 1, + slen - con.cursor_position); + must_resize = must_redraw = 1; + break; + + case SCI_K_BACKSPACE: + if (!con.cursor_position) + break; + + memmove(con.input_text + con.cursor_position - 1, + con.input_text + con.cursor_position, + slen - con.cursor_position + 1); + must_resize = must_redraw = 1; + --con.cursor_position; + break; + + + case SCI_K_ENTER: + done = 1; + break; + + default: + if ((evt.character >= 32) && (evt.character <= 255)) { + memmove(con.input_text + con.cursor_position + 1, + con.input_text + con.cursor_position, + slen - con.cursor_position + 1); + + con.input_text[con.cursor_position] = evt.character; + ++con.cursor_position; + ++slen; + must_redraw = 1; + } + } + } + + if (must_resize) + slen = strlen(con.input_text); + + if (old_pos != con.cursor_position) + must_redraw = 1; + + if (must_rewin) { + int chwidth = CON_BUILTIN_CHARS_WIDTH / state->driver->mode->xfact; + int nr_chars = 320 / chwidth; + + con.cursor_position = slen; + con.input_window = slen - nr_chars + (nr_chars >> 3); + } + + if (must_redraw || must_redraw_text) { + _con_redraw(state, must_redraw_text, must_redraw); + gfxop_update(state); + } + } while (!done); + + retval = con.input_text; + con.input_text = (char *)sci_malloc(64); + con.input_text[0] = 0; + con.input_window = con.input_prompt_pos; + con.cursor_position = 0; + + if (!*retval) { + int hist = -1; + sci_free(retval); + return con_history_get_prev(&hist); + } + /* else */ + con_history_archive(retval); + return retval; +} + +static void +_con_redraw(gfx_state_t *state, int update_display_field, int update_input_field) +{ + int chwidth = CON_BUILTIN_CHARS_WIDTH / state->driver->mode->xfact; + int nr_chars = 320 / chwidth; + int offset = con_displayed_lines; + int pixmap_index = -42; + int must_recompute_pixmap_index = 1; + int max_offset; + con_pos_t pos = con.pos; + rect_t fullbox = con_box(); + int yscale = state->driver->mode->yfact; + int input_field_height = con.input_prompt ? + (con.input_prompt->index_yl + yscale - 1) / yscale : 0; + /* This delta is in "virtual" SCI pixels */ + int input_field_size = con_border_width + input_field_height; + /* Let's consider the bottom padding to be part of the input field */ + + + if (!update_input_field && !update_display_field) + return; + if (!update_input_field) + fullbox.yl -= input_field_size; + + if (!update_display_field) { + fullbox.y = fullbox.yl - input_field_size; + fullbox.yl = input_field_size; + } + + if (con.color_bg.alpha) + _con_draw_bg_pic(state, fullbox); + + /* Draw overlay box */ + gfxop_draw_box(state, fullbox, con.color_bg, con.color_bg, + GFX_BOX_SHADE_FLAT); + + if (update_input_field) { + + if (con_border_width >= 2) { + int y = con_displayed_lines + con_border_width - 2; + + gfxop_draw_line(state, gfx_point(0, y), gfx_point(319, y), + con.color_input, GFX_LINE_MODE_FINE, + GFX_LINE_STYLE_NORMAL); + } + + if (con.input_prompt) { + int promptlen = strlen(CON_GFX_PROMPT); + + if (con.cursor_position - con.input_window < (nr_chars >> 3)) + con.input_window -= (nr_chars >> 1); + else if (con.cursor_position - con.input_window > (nr_chars - (nr_chars >> 3))) + con.input_window += (nr_chars >> 1); + + if (con.input_window < con.input_prompt_pos) + con.input_window = con.input_prompt_pos; + + offset -= input_field_height; + + if (con.input_oncursor) { + gfxop_free_pixmap(state, con.input_oncursor); + con.input_oncursor = NULL; + } + + if (con.input_text) { + char oncursorbuf[2]; + char *postcursor_text = con.input_text + con.cursor_position + 1; + char temp_sep; + + oncursorbuf[1] = 0; + oncursorbuf[0] = temp_sep = con.input_text[con.cursor_position]; + + if (!temp_sep) + oncursorbuf[0] = ' '; /* Draw at least a blank cursor */ + + if (con.input_precursor) { + gfxop_free_pixmap(state, con.input_precursor); + con.input_precursor = NULL; + } + if (con.input_postcursor) { + gfxop_free_pixmap(state, con.input_postcursor); + con.input_postcursor = NULL; + } + + con.input_oncursor = _con_render_text(state, oncursorbuf, -1, + &con.color_input, + &con.color_cursor); + if (con.input_text[0]) + con.input_precursor = _con_render_text(state, + con.input_text, -1, + &con.color_input, + NULL); + if (postcursor_text[-1]) + con.input_postcursor = _con_render_text(state, + postcursor_text, + -1, + &con.color_input, + NULL); + + con.input_text[con.cursor_position] = temp_sep; + } else { + con.input_oncursor = _con_render_text(state, " ", -1, + &con.color_input, + &con.color_cursor); + } + + + if (con.input_window < 0) { + con.input_prompt->xoffset = 0; + con.input_prompt->yoffset = 0; + gfxop_draw_pixmap(state, con.input_prompt, + gfx_rect(0, 0, + con.input_prompt->index_xl, + con.input_prompt->index_yl), + gfx_point(chwidth * (strlen(CON_GFX_PROMPT) + + con.input_window), + offset)); + } + + if (con.input_precursor && con.input_window < con.cursor_position) + gfxop_draw_pixmap(state, con.input_precursor, + gfx_rect(0, 0, + con.input_precursor->index_xl, + con.input_precursor->index_yl), + gfx_point(chwidth * -con.input_window, + offset)); + + if (con.input_postcursor && con.input_window + nr_chars - 1 > + con.cursor_position) { + gfxop_draw_pixmap(state, con.input_postcursor, + gfx_rect(0, 0, + con.input_postcursor->index_xl, + con.input_postcursor->index_yl), + gfx_point(chwidth * (con.cursor_position + 1 + - con.input_window), + offset)); + } + + if (con.input_oncursor) + gfxop_draw_pixmap(state, con.input_oncursor, + gfx_rect(0, 0, + con.input_oncursor->index_xl, + con.input_oncursor->index_yl), + gfx_point(chwidth * (con.cursor_position + - con.input_window), + offset)); + + + } + } else + offset -= input_field_height; + + if (!update_display_field) { + gfxop_update_box(state, fullbox); + return; + } + + max_offset = offset; + + if (con.locked_to_end) { + pos.buf = con_buffer; + pos.offset = 0; + pos.entry = con_top_buffer_entry_nr - 1; + } + + while (pos.buf && offset >= 0) { + con_entry_t *drawme; + int depth = pos.offset; + int line_yl; + + pos.offset = 0; + + if (pos.entry < 0) { + pos.entry = CON_CLUSTER_SIZE - 1; + if (pos.buf) + pos.buf = pos.buf->prev; + + if (!pos.buf) + break; + } + + drawme = &(pos.buf->entries[pos.entry]); + + if (_unfold_graphics(state, drawme, nr_chars)) + must_recompute_pixmap_index = 1; + + if (must_recompute_pixmap_index) { + pixmap_index = drawme->pixmaps_nr - 1; + must_recompute_pixmap_index = 0; + } + + if (pixmap_index < 0) { + pos.entry--; + continue; + } + + while (pixmap_index >= 0 + && depth > (drawme->pixmaps[pixmap_index]->yl + yscale - 1) / yscale) + depth -= (drawme->pixmaps[pixmap_index--]->yl + yscale -1) / yscale; + + if (pixmap_index == -1) { + fprintf(stderr, "ERROR: Offset too great! It was %d in a block of height %d\n", + con.pos.offset, drawme->height); + exit(1); + continue; + } + + line_yl = (drawme->pixmaps[pixmap_index]->yl + yscale - 1) / yscale; + + offset -= line_yl - depth; + depth = line_yl; + + if (offset + depth > max_offset) + depth = max_offset - offset; + + gfxop_draw_pixmap(state, drawme->pixmaps[pixmap_index], + gfx_rect(0, 0, + drawme->pixmaps[pixmap_index]->index_xl, depth), + gfx_point(0, offset)); + /* + ** TODO: Insert stuff into overwrapped lines ** + if (pixmap_index) + gfxop_draw_line(state, gfx_rect(chwidth - 2, offset, + chwidth - 2, offset + depth), + con.color_text, GFX_LINE_MODE_FINE, GFX_LINE_STYLE_NORMAL); + */ + + if (!pixmap_index) { + pos.entry--; + must_recompute_pixmap_index = 1; + } else + --pixmap_index; + } + + gfxop_update_box(state, fullbox); +} + +static void +_con_free_entry_pixmaps(gfx_state_t *state, con_entry_t *entry) +{ + int j; + + if (entry->pixmaps) { + for (j = 0; j < entry->pixmaps_nr; j++) + gfxop_free_pixmap(state, entry->pixmaps[j]); + sci_free(entry->pixmaps); + entry->pixmaps_nr = 0; + entry->pixmaps = NULL; + } +} + + +static void +_free_con_buffer(con_buffer_t *buf); + +static void +free_con_buffer(con_buffer_t *buf) +/* Frees a con buffer and all of its predecessors */ +{ + /* First, make sure we're not destroying the current display */ + if (!con.locked_to_end) { + con_buffer_t *seeker = con.pos.buf; + while (seeker && seeker != buf) + seeker = seeker->prev; + + if (seeker) { + if (seeker->prev) + con.pos.buf = seeker->next; + else + con.locked_to_end = 1; + } + } + _free_con_buffer(buf); +} + +static void +_free_con_buffer(con_buffer_t *buf) +{ + int i; + + if (buf) { + con_buffer_t *prev = buf->prev; + + if (buf->next) + buf->next->prev = NULL; + for (i = 0; i < CON_CLUSTER_SIZE; i++) + if (CON_ENTRY_USED(buf->entries[i])) { + if (buf->entries[i].text) + sci_free(buf->entries[i].text); + _con_free_entry_pixmaps(con_last_gfx_state, &buf->entries[i]); + + buf->entries[i].height = -1; + } + sci_free(buf); + --con_buffer_clusters; + + if (prev) + _free_con_buffer(prev); + } +} + + +byte con_builtin_font_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, + 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e, + 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, + 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, + 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0xd6, 0x10, 0x38, + 0x10, 0x38, 0x7c, 0xfe, 0xfe, 0x7c, 0x10, 0x38, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, + 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, + 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, + 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, + 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78, + 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, + 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0, + 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0, + 0x18, 0xdb, 0x3c, 0xe7, 0xe7, 0x3c, 0xdb, 0x18, + 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00, + 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00, + 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, + 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00, + 0x3e, 0x61, 0x3c, 0x66, 0x66, 0x3c, 0x86, 0x7c, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00, + 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff, + 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, + 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, + 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, 0x00, + 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, + 0x18, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x18, 0x00, + 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00, + 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00, + 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, + 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, + 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, + 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, + 0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, + 0x7c, 0xc6, 0x06, 0x1c, 0x30, 0x66, 0xfe, 0x00, + 0x7c, 0xc6, 0x06, 0x3c, 0x06, 0xc6, 0x7c, 0x00, + 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00, + 0xfe, 0xc0, 0xc0, 0xfc, 0x06, 0xc6, 0x7c, 0x00, + 0x38, 0x60, 0xc0, 0xfc, 0xc6, 0xc6, 0x7c, 0x00, + 0xfe, 0xc6, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00, + 0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0x7c, 0x00, + 0x7c, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, + 0x06, 0x0c, 0x18, 0x30, 0x18, 0x0c, 0x06, 0x00, + 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, + 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, + 0x7c, 0xc6, 0x0c, 0x18, 0x18, 0x00, 0x18, 0x00, + 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00, + 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00, + 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00, + 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, + 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00, + 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00, + 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3a, 0x00, + 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, + 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, + 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, + 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, + 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00, + 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, + 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xce, 0x7c, 0x0e, + 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00, + 0x3c, 0x66, 0x30, 0x18, 0x0c, 0x66, 0x3c, 0x00, + 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, + 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, + 0xc6, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0xc6, 0x00, + 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x00, + 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00, + 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, + 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, + 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0xe0, 0x60, 0x7c, 0x66, 0x66, 0x66, 0xdc, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc6, 0x7c, 0x00, + 0x1c, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, + 0x3c, 0x66, 0x60, 0xf8, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00, + 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x06, 0x00, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, + 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00, + 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6, 0x00, + 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e, + 0x00, 0x00, 0xdc, 0x76, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x7e, 0xc0, 0x7c, 0x06, 0xfc, 0x00, + 0x30, 0x30, 0xfc, 0x30, 0x30, 0x36, 0x1c, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, + 0x00, 0x00, 0xc6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, + 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0xfc, + 0x00, 0x00, 0x7e, 0x4c, 0x18, 0x32, 0x7e, 0x00, + 0x0e, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0e, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x70, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x70, 0x00, + 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x18, 0x00, + 0x18, 0x18, 0x7e, 0xc0, 0xc0, 0x7e, 0x18, 0x18, + 0x38, 0x6c, 0x64, 0xf0, 0x60, 0x66, 0xfc, 0x00, + 0x00, 0xc6, 0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0x00, + 0x66, 0x66, 0x3c, 0x7e, 0x18, 0x7e, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, + 0x3e, 0x61, 0x3c, 0x66, 0x66, 0x3c, 0x86, 0x7c, + 0x00, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x81, 0x9d, 0xa1, 0xa1, 0x9d, 0x81, 0x7e, + 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, + 0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x81, 0xb9, 0xa5, 0xb9, 0xa5, 0x81, 0x7e, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x7e, 0x00, + 0x78, 0x0c, 0x18, 0x30, 0x7c, 0x00, 0x00, 0x00, + 0x78, 0x0c, 0x38, 0x0c, 0x78, 0x00, 0x00, 0x00, + 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0xc0, + 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0x38, + 0x18, 0x38, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, + 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00, + 0x63, 0xe6, 0x6c, 0x7a, 0x36, 0x6a, 0xdf, 0x06, + 0x63, 0xe6, 0x6c, 0x7e, 0x33, 0x66, 0xcc, 0x0f, + 0xe1, 0x32, 0xe4, 0x3a, 0xf6, 0x2a, 0x5f, 0x86, + 0x18, 0x00, 0x18, 0x18, 0x30, 0x63, 0x3e, 0x00, + 0x18, 0x0c, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x00, + 0x30, 0x60, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x00, + 0x7c, 0x82, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x00, + 0x76, 0xdc, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x00, + 0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, + 0x38, 0x6c, 0x7c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, + 0x3e, 0x6c, 0xcc, 0xfe, 0xcc, 0xcc, 0xce, 0x00, + 0x7c, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x0c, 0x78, + 0x30, 0x18, 0xfe, 0xc0, 0xfc, 0xc0, 0xfe, 0x00, + 0x18, 0x30, 0xfe, 0xc0, 0xf8, 0xc0, 0xfe, 0x00, + 0x7c, 0x82, 0xfe, 0xc0, 0xfc, 0xc0, 0xfe, 0x00, + 0xc6, 0x00, 0xfe, 0xc0, 0xfc, 0xc0, 0xfe, 0x00, + 0x30, 0x18, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x0c, 0x18, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x3c, 0x42, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x66, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0xf8, 0x6c, 0x66, 0xf6, 0x66, 0x6c, 0xf8, 0x00, + 0x76, 0xdc, 0x00, 0xe6, 0xf6, 0xde, 0xce, 0x00, + 0x0c, 0x06, 0x38, 0x6c, 0xc6, 0x6c, 0x38, 0x00, + 0x30, 0x60, 0x38, 0x6c, 0xc6, 0x6c, 0x38, 0x00, + 0x7c, 0x82, 0x38, 0x6c, 0xc6, 0x6c, 0x38, 0x00, + 0x76, 0xdc, 0x38, 0x6c, 0xc6, 0x6c, 0x38, 0x00, + 0xc6, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x00, + 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, 0x00, + 0x3a, 0x6c, 0xce, 0xd6, 0xe6, 0x6c, 0xb8, 0x00, + 0x60, 0x30, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x18, 0x30, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x7c, 0x82, 0x00, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x0c, 0x18, 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x00, + 0xf0, 0x60, 0x7c, 0x66, 0x7c, 0x60, 0xf0, 0x00, + 0x78, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xcc, 0x00, + 0x30, 0x18, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0x18, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0x7c, 0x82, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0x76, 0xdc, 0x7c, 0x06, 0x7e, 0xc6, 0x7e, 0x00, + 0xc6, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0x7e, 0x12, 0xfe, 0x90, 0xfe, 0x00, + 0x00, 0x00, 0x7e, 0xc0, 0xc0, 0x7e, 0x0c, 0x38, + 0x30, 0x18, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, + 0x0c, 0x18, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, + 0x7c, 0x82, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, + 0xc6, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, + 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00, + 0x0c, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00, + 0x7c, 0x82, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x30, 0x7e, 0x0c, 0x7c, 0xcc, 0xcc, 0x78, 0x00, + 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x00, + 0x30, 0x18, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x0c, 0x18, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x7c, 0x82, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x76, 0xdc, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x00, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x02, 0x7c, 0xce, 0xd6, 0xe6, 0x7c, 0x80, + 0x60, 0x30, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x18, 0x30, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x78, 0x84, 0x00, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x18, 0x30, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0xfc, + 0xe0, 0x60, 0x7c, 0x66, 0x66, 0x7c, 0x60, 0xf0, + 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0xfc +}; + +#endif /* !WANT_CONSOLE */ diff --git a/engines/sci/gfx/gfx_crossblit.c b/engines/sci/gfx/gfx_crossblit.c new file mode 100644 index 0000000000..af55a90490 --- /dev/null +++ b/engines/sci/gfx/gfx_crossblit.c @@ -0,0 +1,107 @@ +/*************************************************************************** + gfx_crossblit.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +/* This file isn't used directly; rather, it's included by a different file. */ +/* Note that memcpy() is assumed to be an inlineable built-in. If it isn't, +** performance will suck... badly. +*/ +/* Config parameters: +** FUNCTION_NAME: Name of the blitter function +** USE_PRIORITY: Whether to care about the priority buffer +** BYTESPP: Bytes per pixel +*/ + +/* set optimisations for Win32: */ +/* g on: enable global optimizations */ +/* t on: use fast code */ +/* y on: suppress creation of frame pointers on stack */ +/* s off: disable minimize size code */ +#ifdef _WIN32 +# include +# ifndef SATISFY_PURIFY +# pragma optimize( "s", off ) +# pragma optimize( "gty", on ) +# pragma intrinsic( memcpy, memset ) +# endif +#endif + +static void FUNCTION_NAME(byte *dest, byte *src, int bytes_per_dest_line, int bytes_per_src_line, + int xl, int yl, byte *alpha, int bytes_per_alpha_line, int bytes_per_alpha_pixel, + unsigned int alpha_test_mask, unsigned int alpha_min +#ifdef USE_PRIORITY + , byte *priority_buffer, int bytes_per_priority_line, int bytes_per_priority_pixel, int priority +#endif /* USE_PRIORITY */ + ) +{ + int x, y; + int alpha_end = xl * bytes_per_alpha_pixel; + + for (y = 0; y < yl; y++) { + int pixel_offset = 0; + int alpha_offset = 0; +#ifdef USE_PRIORITY + int priority_offset = 0; +#endif /* USE_PRIORITY */ + + for (x = 0; x < alpha_end; x += bytes_per_alpha_pixel) { + if ((alpha_test_mask & alpha[x]) +#ifdef REVERSE_ALPHA + >= +#else + < +#endif + alpha_min) +#ifdef USE_PRIORITY + if (priority_buffer[priority_offset] <= priority) { + priority_buffer[priority_offset] = priority; +#endif /* USE_PRIORITY */ + memcpy(dest + pixel_offset, src + pixel_offset, BYTESPP); +#ifdef USE_PRIORITY + } +#endif /* USE_PRIORITY */ + + pixel_offset += BYTESPP; + alpha_offset += bytes_per_alpha_pixel; +#ifdef USE_PRIORITY + priority_offset += bytes_per_priority_pixel; +#endif /* USE_PRIORITY */ + } + + dest += bytes_per_dest_line; + src += bytes_per_src_line; + alpha += bytes_per_alpha_line; +#ifdef USE_PRIORITY + priority_buffer += bytes_per_priority_line; +#endif /* USE_PRIORITY */ + } +} + +/* reset to original optimisations for Win32: */ +/* (does not reset intrinsics) */ +#ifdef _WIN32 +# pragma optimize( "", on ) +#endif diff --git a/engines/sci/gfx/gfx_line.c b/engines/sci/gfx/gfx_line.c new file mode 100644 index 0000000000..01b6a8fd2a --- /dev/null +++ b/engines/sci/gfx/gfx_line.c @@ -0,0 +1,96 @@ +/*************************************************************************** + gfx_line.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#define LINEMACRO(startx, starty, deltalinear, deltanonlinear, linearvar, nonlinearvar, \ + linearend, nonlinearstart, linearmod, nonlinearmod) \ + x = (startx); y = (starty); \ + incrNE = ((deltalinear) > 0)? (deltalinear) : -(deltalinear); \ + incrNE <<= 1; \ + deltanonlinear <<= 1; \ + incrE = ((deltanonlinear) > 0) ? -(deltanonlinear) : (deltanonlinear); \ + d = nonlinearstart-1; \ + while (linearvar != (linearend)) { \ + memcpy(buffer + linewidth * y + x, &color, PIXELWIDTH); \ + linearvar += linearmod; \ + if ((d+=incrE) < 0) { \ + d += incrNE; \ + nonlinearvar += nonlinearmod; \ + }; \ + }; \ + memcpy(buffer + linewidth * y + x, &color, PIXELWIDTH); + + +static inline +void DRAWLINE_FUNC(byte *buffer, int linewidth, point_t start, point_t end, unsigned int color) +{ + int dx, dy, incrE, incrNE, d, finalx, finaly; + int x = start.x; + int y = start.y; + dx = end.x - start.x; + dy = end.y - start.y; + finalx = end.x; + finaly = end.y; +#ifdef WORDS_BIGENDIAN + color = GUINT32_SWAP_LE_BE_CONSTANT(color); +#endif + dx = abs(dx); + dy = abs(dy); + + if (dx > dy) { + if (finalx < x) { + if (finaly < y) { /* llu == left-left-up */ + LINEMACRO(x, y, dx, dy, x, y, finalx, dx, -PIXELWIDTH, -1); + } else { /* lld */ + LINEMACRO(x, y, dx, dy, x, y, finalx, dx, -PIXELWIDTH, 1); + } + } else { /* x1 >= x */ + if (finaly < y) { /* rru */ + LINEMACRO(x, y, dx, dy, x, y, finalx, dx, PIXELWIDTH, -1); + } else { /* rrd */ + LINEMACRO(x, y, dx, dy, x, y, finalx, dx, PIXELWIDTH, 1); + } + } + } else { /* dx <= dy */ + if (finaly < y) { + if (finalx < x) { /* luu */ + LINEMACRO(x, y, dy, dx, y, x, finaly, dy, -1, -PIXELWIDTH); + } else { /* ruu */ + LINEMACRO(x, y, dy, dx, y, x, finaly, dy, -1, PIXELWIDTH); + } + } else { /* y1 >= y */ + if (finalx < x) { /* ldd */ + LINEMACRO(x, y, dy, dx, y, x, finaly, dy, 1, -PIXELWIDTH); + } else { /* rdd */ + LINEMACRO(x, y, dy, dx, y, x, finaly, dy, 1, PIXELWIDTH); + } + } + } +} + + + +#undef LINEMACRO diff --git a/engines/sci/gfx/gfx_pixmap_scale.c b/engines/sci/gfx/gfx_pixmap_scale.c new file mode 100644 index 0000000000..c288553eb1 --- /dev/null +++ b/engines/sci/gfx/gfx_pixmap_scale.c @@ -0,0 +1,497 @@ +/* Required defines: +** FUNCNAME: Function name +** SIZETYPE: Type used for each pixel +** EXTRA_BYTE_OFFSET: Extra source byte offset for copying (used on big-endian machines in 24 bit mode) +*/ + +/* set optimisations for Win32: */ +/* g on: enable global optimizations */ +/* t on: use fast code */ +/* y on: suppress creation of frame pointers on stack */ +/* s off: disable minimize size code */ +#ifdef _WIN32 +# include +# ifndef SATISFY_PURIFY +# pragma optimize( "s", off ) +# pragma optimize( "gty", on ) +# pragma intrinsic( memcpy, memset ) +# endif +#endif + +#include + +#define EXTEND_COLOR(x) (unsigned) ((((unsigned) x) << 24) | (((unsigned) x) << 16) | (((unsigned) x) << 8) | ((unsigned) x)) +#define PALETTE_MODE mode->palette + +void +FUNCNAME(gfx_mode_t *mode, gfx_pixmap_t *pxm, int scale) +{ + SIZETYPE result_colors[GFX_PIC_COLORS]; + SIZETYPE alpha_color = 0xffffffff & mode->alpha_mask; + SIZETYPE alpha_ormask = 0; + int xfact = (scale)? mode->xfact: 1; + int yfact = (scale)? mode->yfact: 1; + int widthc, heightc; /* Width duplication counter */ + int line_width = xfact * pxm->index_xl; + int bytespp = mode->bytespp; + int x, y; + int i; + byte byte_transparent = (mode->flags & GFX_MODE_FLAG_REVERSE_ALPHA)? 0 : 255; + byte byte_opaque = (mode->flags & GFX_MODE_FLAG_REVERSE_ALPHA)? 255 : 0; + byte *src = pxm->index_data; + byte *dest = pxm->data; + byte *alpha_dest = pxm->alpha_map; + int using_alpha = pxm->color_key != GFX_PIXMAP_COLOR_KEY_NONE; + int separate_alpha_map = (!mode->alpha_mask) && using_alpha; + + if (mode->flags & GFX_MODE_FLAG_REVERSE_ALPHA) { + alpha_ormask = alpha_color; + alpha_color = 0; + } + + assert(bytespp == COPY_BYTES); + + if (separate_alpha_map && !alpha_dest) + alpha_dest = pxm->alpha_map = (byte*)sci_malloc(pxm->index_xl * xfact * pxm->index_yl * yfact); + + /* Calculate all colors */ + for (i = 0; i < pxm->colors_nr; i++) { + int col; + + if (PALETTE_MODE) + col = pxm->colors[i].global_index; + else { + col = mode->red_mask & ((EXTEND_COLOR(pxm->colors[i].r)) >> mode->red_shift); + col |= mode->green_mask & ((EXTEND_COLOR(pxm->colors[i].g)) >> mode->green_shift); + col |= mode->blue_mask & ((EXTEND_COLOR(pxm->colors[i].b)) >> mode->blue_shift); + col |= alpha_ormask; + } + result_colors[i] = col; + } + + if (!separate_alpha_map && pxm->color_key != GFX_PIXMAP_COLOR_KEY_NONE) + result_colors[pxm->color_key] = alpha_color; + + src = pxm->index_data; /* Workaround for gcc 4.2.3 bug on EMT64 */ + for (y = 0; y < pxm->index_yl; y++) { + byte *prev_dest = dest; + byte *prev_alpha_dest = alpha_dest; + + for (x = 0; x < pxm->index_xl; x++) { + int isalpha; + SIZETYPE col = result_colors[isalpha = *src++] << (EXTRA_BYTE_OFFSET * 8); + isalpha = (isalpha == pxm->color_key) && using_alpha; + + /* O(n) loops. There is an O(ln(n)) algorithm for this, but its slower for small n (which we're optimizing for here). + ** And, anyway, most of the time is spent in memcpy() anyway. */ + + for (widthc = 0; widthc < xfact; widthc++) { + memcpy(dest, &col, COPY_BYTES); + dest += COPY_BYTES; + } + + if (separate_alpha_map) { /* Set separate alpha map */ + memset(alpha_dest, (isalpha)? byte_transparent : byte_opaque, xfact); + alpha_dest += xfact; + } + } + + /* Copies each line. O(n) iterations; again, this could be optimized to O(ln(n)) for very high resolutions, + ** but that wouldn't really help that much, as the same amount of data still would have to be transferred. + */ + for (heightc = 1; heightc < yfact; heightc++) { + memcpy(dest, prev_dest, line_width * bytespp); + dest += line_width * bytespp; + if (separate_alpha_map) { + memcpy(alpha_dest, prev_alpha_dest, line_width); + alpha_dest += line_width; + } + } + } +} + + + + +/* linear filter: Macros (in reverse order) */ + +#define X_CALC_INTENSITY_NORMAL (ctexel[i] << 16) + ((linecolor[i])*(256-column_valuator)) + ((othercolumn[i]*column_valuator))*(256-line_valuator) +#define X_CALC_INTENSITY_CENTER (ctexel[i] << 16) + ((linecolor[i])*(256-column_valuator)) + +#define WRITE_XPART(X_CALC_INTENSITY, DO_X_STEP) \ + for (subx = 0; subx < ((DO_X_STEP)? (xfact >> 1) : 1); subx++) { \ + unsigned int intensity; \ + wrcolor = 0; \ + for (i = 0; i < 3; i++) { \ + intensity = X_CALC_INTENSITY; \ + wrcolor |= (intensity >> shifts[i]) & masks[i]; \ + } \ + i = 3; \ + intensity = X_CALC_INTENSITY; \ + if (inverse_alpha) \ + intensity = ~intensity; \ + wrcolor |= (intensity >> shifts[i]) & masks[i]; \ + if (separate_alpha_map) \ + *alpha_wrpos++ = intensity >> 24; \ + wrcolor <<= (EXTRA_BYTE_OFFSET * 8); \ + memcpy(wrpos, &wrcolor, COPY_BYTES); \ + wrpos += COPY_BYTES; \ + if (DO_X_STEP) \ + column_valuator -= column_step; \ + } \ + if (DO_X_STEP) \ + column_step = -column_step +/* End of macro definition */ + + +#define Y_CALC_INTENSITY_CENTER 0 +#define Y_CALC_INTENSITY_NORMAL otherline[i]*line_valuator + +#define WRITE_YPART(DO_Y_STEP, LINE_COLOR) \ + for (suby = 0; suby < ((DO_Y_STEP)? yfact >> 1 : 1); suby++) { \ + int column_valuator = column_step? 128 - (column_step >> 1) : 256; \ + int linecolor[4]; \ + int othercolumn[4]; \ + int i; \ + SIZETYPE wrcolor; \ + wrpos = sublinepos; \ + alpha_wrpos = alpha_sublinepos; \ + for (i = 0; i < 4; i++) \ + linecolor[i] = LINE_COLOR; \ + /*-- left half --*/ \ + MAKE_PIXEL((x == 0), othercolumn, ctexel, src[-1]); \ + WRITE_XPART(X_CALC_INTENSITY_NORMAL, 1); \ + column_valuator -= column_step; \ + /*-- center --*/ \ + if (xfact & 1) { \ + WRITE_XPART(X_CALC_INTENSITY_CENTER, 0); \ + } \ + /*-- right half --*/ \ + MAKE_PIXEL((x+1 == pxm->index_xl), othercolumn, ctexel, src[+1]); \ + WRITE_XPART(X_CALC_INTENSITY_NORMAL, 1); \ + if (DO_Y_STEP) \ + line_valuator -= line_step; \ + sublinepos += pxm->xl * bytespp; \ + alpha_sublinepos += pxm->xl; \ + } \ + if (DO_Y_STEP) \ + line_step = -line_step +/* End of macro definition */ + + + + +void +FUNCNAME_LINEAR(gfx_mode_t *mode, gfx_pixmap_t *pxm, int scale) +{ + int xfact = mode->xfact; + int yfact = mode->yfact; + int line_step = (yfact < 2)? 0 : 256 / (yfact & ~1); + int column_step = (xfact < 2)? 0 : 256 / (xfact & ~1); + int bytespp = mode->bytespp; + byte *src = pxm->index_data; + byte *dest = pxm->data; + byte *alpha_dest = pxm->alpha_map; + int using_alpha = pxm->color_key != GFX_PIXMAP_COLOR_KEY_NONE; + int separate_alpha_map = (!mode->alpha_mask) && using_alpha; + unsigned int masks[4], shifts[4], zero[3]; + int x,y; + byte inverse_alpha = mode->flags & GFX_MODE_FLAG_REVERSE_ALPHA; + + zero[0] = 255; + zero[1] = zero[2] = 0; + + if (separate_alpha_map) { + masks[3] = 0; + shifts[3] = 24; + } + + assert(bytespp == COPY_BYTES); + assert(!PALETTE_MODE); + + masks[0] = mode->red_mask; + masks[1] = mode->green_mask; + masks[2] = mode->blue_mask; + masks[3] = mode->alpha_mask; + shifts[0] = mode->red_shift; + shifts[1] = mode->green_shift; + shifts[2] = mode->blue_shift; + shifts[3] = mode->alpha_shift; + + if (separate_alpha_map && !alpha_dest) + alpha_dest = pxm->alpha_map = (byte*)sci_malloc(pxm->index_xl * xfact * pxm->index_yl * yfact); + + for (y = 0; y < pxm->index_yl; y++) { + byte *linepos = dest; + byte *alpha_linepos = alpha_dest; + + for (x = 0; x < pxm->index_xl; x++) { + int otherline[4]; /* the above line or the line below */ + int ctexel[4]; /* Current texel */ + int subx, suby; + int line_valuator = line_step? 128 - (line_step >> 1) : 256; + byte *wrpos, *alpha_wrpos; + byte *sublinepos = linepos; + byte *alpha_sublinepos = alpha_linepos; + + ctexel[0] = ctexel[1] = ctexel[2] = ctexel[3] = 0; + +#define MAKE_PIXEL(cond, rec, other, nr) \ + if ((cond) || (using_alpha && nr == pxm->color_key)) { \ + rec[0] = other[0] - ctexel[0]; \ + rec[1] = other[1] - ctexel[1]; \ + rec[2] = other[2] - ctexel[2]; \ + rec[3] = 0xffff - ctexel[3]; \ + } else { \ + rec[0] = (EXTEND_COLOR(pxm->colors[nr].r) >> 16) - ctexel[0]; \ + rec[1] = (EXTEND_COLOR(pxm->colors[nr].g) >> 16) - ctexel[1]; \ + rec[2] = (EXTEND_COLOR(pxm->colors[nr].b) >> 16) - ctexel[2]; \ + rec[3] = 0 - ctexel[3]; \ + } + + MAKE_PIXEL(0, ctexel, zero, *src); + + /*-- Upper half --*/ + MAKE_PIXEL((y == 0), otherline, ctexel, src[-pxm->index_xl]); + WRITE_YPART(1, Y_CALC_INTENSITY_NORMAL); + + if (yfact & 1) { + WRITE_YPART(0, Y_CALC_INTENSITY_CENTER); + } + + /*-- Lower half --*/ + line_valuator -= line_step; + MAKE_PIXEL((y+1 == pxm->index_yl), otherline, ctexel, src[pxm->index_xl]); + WRITE_YPART(1, Y_CALC_INTENSITY_NORMAL); + + src++; + linepos += xfact * bytespp; + alpha_linepos += xfact; + } + + dest += pxm->xl * yfact * bytespp; + alpha_dest += pxm->xl * yfact; + } +} + + + +/*----------------------*/ +/*** Trilinear filter ***/ +/*----------------------*/ + + +#ifndef GFX_GET_PIXEL_DELTA +#define GFX_GET_PIXEL_DELTA +static inline void +gfx_get_pixel_delta(unsigned int *color, int *delta, unsigned int *pixel0, unsigned int *pixel1) +{ + int j; + int transp0 = pixel0[3] == 0xffffff; + int transp1 = pixel1[3] == 0xffffff; + + if (transp0 && !transp1) { /* Transparent -> Opaque */ + memset(delta, 0, sizeof(int) * 3); + delta[3] = ((pixel1[3] >> 8) - (pixel0[3] >> 8)); + memcpy(color, pixel1, sizeof(int) * 3); + color[3] = 0xffffff; + } else if (!transp0 && transp1) { /* Opaque -> Transparent */ + memset(delta, 0, sizeof(int) * 3); + delta[3] = ((pixel1[3] >> 8) - (pixel0[3] >> 8)); + memcpy(color, pixel0, sizeof(int) * 4); + } else if (transp0 && transp1) { /* Transparent */ + delta[3] = 0; + color[3] = 0xffffff; + } else { /* Opaque */ + memcpy(color, pixel0, sizeof(int) * 4); + for (j = 0; j < 4; j++) + delta[j] = ((pixel1[j] >> 8) - (pixel0[j] >> 8)); + } +} + + +static inline void +gfx_apply_delta(unsigned int *color, int *delta, int factor) +{ + int i; + for (i = 0; i < 4; i++) + color[i] += delta[i] * factor; +} +#endif + +#define MAKE_PIXEL_TRILINEAR(cond, rec, nr) \ + if (!(cond) || (using_alpha && nr == pxm->color_key)) { \ + rec[0] = 0; \ + rec[1] = 0; \ + rec[2] = 0; \ + rec[3] = 0xffffff; \ + } else { \ + rec[0] = (EXTEND_COLOR(pxm->colors[nr].r) >> 8); \ + rec[1] = (EXTEND_COLOR(pxm->colors[nr].g) >> 8); \ + rec[2] = (EXTEND_COLOR(pxm->colors[nr].b) >> 8); \ + rec[3] = 0; \ + } + +#define REVERSE_ALPHA(foo) ((inverse_alpha)? ~(foo) : (foo)) + +void +FUNCNAME_TRILINEAR(gfx_mode_t *mode, gfx_pixmap_t *pxm, int scale) +{ + int xfact = mode->xfact; + int yfact = mode->yfact; + int line_step = (yfact < 2)? 0 : 256 / yfact; + int column_step = (xfact < 2)? 0 : 256 / xfact; + int bytespp = mode->bytespp; + byte *src = pxm->index_data; + byte *dest = pxm->data; + byte *alpha_dest = pxm->alpha_map; + int using_alpha = pxm->color_key != GFX_PIXMAP_COLOR_KEY_NONE; + int separate_alpha_map = (!mode->alpha_mask) && using_alpha; + unsigned int masks[4], shifts[4]; + unsigned int pixels[4][4]; + /* 0 1 + ** 2 3 */ + int x,y; + byte inverse_alpha = mode->flags & GFX_MODE_FLAG_REVERSE_ALPHA; + + if (separate_alpha_map) { + masks[3] = 0; + shifts[3] = 24; + } + + assert(bytespp == COPY_BYTES); + assert(!PALETTE_MODE); + + masks[0] = mode->red_mask; + masks[1] = mode->green_mask; + masks[2] = mode->blue_mask; + masks[3] = mode->alpha_mask; + shifts[0] = mode->red_shift; + shifts[1] = mode->green_shift; + shifts[2] = mode->blue_shift; + shifts[3] = mode->alpha_shift; + + if (!(pxm->index_xl && pxm->index_yl)) + return; /* Duh. */ + + if (separate_alpha_map && !alpha_dest) + alpha_dest = pxm->alpha_map = (byte*)sci_malloc(pxm->index_xl * xfact * pxm->index_yl * yfact); + + src -= pxm->index_xl + 1; + + for (y = 0; y <= pxm->index_yl; y++) { + byte *y_dest_backup = dest; + byte *y_alpha_dest_backup = alpha_dest; + int y_valuator = (y > 0)? 0 : 128; + int yc_count; + + + if (y == 0) + yc_count = yfact >> 1; + else if (y == pxm->index_yl) + yc_count = (yfact + 1) >> 1; + else + yc_count = yfact; + + if (yfact & 1) + y_valuator += line_step >> 1; + + for (x = 0; x <= pxm->index_xl; x++) { + byte *x_dest_backup = dest; + byte *x_alpha_dest_backup = alpha_dest; + int x_valuator = (x > 0)? 0 : 128; + int xc_count; + unsigned int leftcolor[4], rightcolor[4]; + int leftdelta[4], rightdelta[4]; + int xc, yc; + + if (x == 0) + xc_count = xfact >> 1; + else if (x == pxm->index_xl) + xc_count = (xfact + 1) >> 1; + else + xc_count = xfact; + + if (xfact & 1) + x_valuator += column_step >> 1; + + MAKE_PIXEL_TRILINEAR((y && x), pixels[0], *src); + MAKE_PIXEL_TRILINEAR((y && (x < pxm->index_xl)), pixels[1], src[1]); + MAKE_PIXEL_TRILINEAR(((y < pxm->index_yl) && x), pixels[2], src[pxm->index_xl]); + MAKE_PIXEL_TRILINEAR(((y < pxm->index_yl) && (x < pxm->index_xl)), + pixels[3], src[pxm->index_xl + 1]); + + /* OptimizeMe */ + + gfx_get_pixel_delta(leftcolor, leftdelta, pixels[0], pixels[2]); + gfx_get_pixel_delta(rightcolor, rightdelta, pixels[1], pixels[3]); + gfx_apply_delta(leftcolor, leftdelta, y_valuator); + gfx_apply_delta(rightcolor, rightdelta, y_valuator); + + for (yc = 0; yc < yc_count; yc++) { + unsigned int color[4]; + int delta[4]; + byte *yc_dest_backup = dest; + byte *yc_alpha_dest_backup = alpha_dest; + + gfx_get_pixel_delta(color, delta, leftcolor, rightcolor); + + gfx_apply_delta(color, delta, x_valuator); + + for (xc = 0; xc < xc_count; xc++) { + SIZETYPE wrcolor; + int i; + wrcolor = 0; + + for (i = 0; i < 3; i++) + wrcolor |= ((color[i] << 8) >> shifts[i]) & masks[i]; + + if (separate_alpha_map) { + *alpha_dest++ = REVERSE_ALPHA(color[3] >> 16); + } else + wrcolor |= REVERSE_ALPHA((color[3] << 8) >> shifts[3]) & masks[3]; + + wrcolor <<= (EXTRA_BYTE_OFFSET * 8); + + memcpy(dest, &wrcolor, COPY_BYTES); + dest += COPY_BYTES; + gfx_apply_delta(color, delta, column_step); + } + gfx_apply_delta(leftcolor, leftdelta, line_step); + gfx_apply_delta(rightcolor, rightdelta, line_step); + + dest = yc_dest_backup + pxm->index_xl * xfact * COPY_BYTES; + alpha_dest = yc_alpha_dest_backup + pxm->index_xl * xfact; + } + + dest = x_dest_backup + xc_count * COPY_BYTES; + alpha_dest = x_alpha_dest_backup + xc_count; + + if (x < pxm->index_xl) + src++; + } + dest = y_dest_backup + pxm->index_xl * xfact * yc_count * COPY_BYTES; + alpha_dest = y_alpha_dest_backup + pxm->index_xl * xfact * yc_count; + } +} + +#undef REVERSE_ALPHA +#undef WRITE_YPART +#undef Y_CALC_INTENSITY_CENTER +#undef Y_CALC_INTENSITY_NORMAL +#undef WRITE_XPART +#undef X_CALC_INTENSITY_CENTER +#undef X_CALC_INTENSITY_NORMAL +#undef MAKE_PIXEL_TRILINEAR +#undef MAKE_PIXEL +#undef FUNCNAME +#undef FUNCNAME_LINEAR +#undef FUNCNAME_TRILINEAR +#undef SIZETYPE +#undef EXTEND_COLOR + +/* reset to original optimisations for Win32: */ +/* (does not reset intrinsics) */ +#ifdef _WIN32 +# pragma optimize( "", on ) +#endif diff --git a/engines/sci/gfx/gfx_res_options.c b/engines/sci/gfx/gfx_res_options.c new file mode 100644 index 0000000000..cec2c1f26a --- /dev/null +++ b/engines/sci/gfx/gfx_res_options.c @@ -0,0 +1,652 @@ +/*************************************************************************** + gfx_res_options.c Copyright (C) 2002 Christoph Reichenbach + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + + Please contact the maintainer for any program-related bug reports or + inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include +#include + +#include + +/*#define DEBUG*/ + +static gfx_res_pattern_list_t* +pattern_list_insert(gfx_res_pattern_list_t *list, int min, int max) +{ + gfx_res_pattern_list_t *retval = (gfx_res_pattern_list_t*)sci_malloc(sizeof(gfx_res_pattern_list_t)); + retval->pattern.min = min; + retval->pattern.max = max; + retval->next = list; + + return retval; +} + +static int +pattern_list_len(gfx_res_pattern_list_t *list) +{ + int v = 0; + while (list) { + ++v; + list = list->next; + } + + return v; +} + +static void +pattern_list_flatten(gfx_res_pattern_t *dest, gfx_res_pattern_list_t *list) +{ + while (list) { + *dest++ = list->pattern; + list = list->next; + } +} + +static inline void +pattern_list_free(gfx_res_pattern_list_t *list) +{ + if (list) + pattern_list_free(list->next); + + free(list); +} + + +static inline int +extract_pattern(gfx_res_pattern_list_t **destp, + char *src, int offset) +{ + char *src_orig = src - offset; + int final = 0; + int wildcard = 0; + + while (!final) { + char *end = strchr(src, ','); + if (end) + *end = 0; + else + final = 1; + + while (*src && isblank(*src)) + ++src; + + if (*src == '*' + || *src == '_') { + wildcard = 1; + ++src; + } else if (*src == '.' || isdigit(*src)) { + char *endp; + int start = strtol(src, &endp, 0); + + if (*src == '.' + && src[1] == '.') { + start = GFX_RES_PATTERN_MIN; + endp = src; + } + + src = endp; + + while (*src && isblank(*src)) + ++src; + + if (*src) { + int stop; + if (*src == '.' + && src[1] == '.') { + + src += 2; + while (*src && isblank(*src)) + ++src; + + if (!*src) + stop = GFX_RES_PATTERN_MAX; + else if (!isdigit(*src)) { + if (end) + *end = ','; + goto lexical_error_label; + } + + stop = strtol(src, &endp, 0); + src = endp; + + *destp = pattern_list_insert(*destp, + start, stop); + + } else { /* No ellipsis */ + if (end) + *end = ','; + goto lexical_error_label; + } + } else /* End of sub-pattern */ + *destp = pattern_list_insert(*destp, + start, start); + + while (*src && isblank(*src)) + ++src; + + if (*src) { + if (end) + *end = ','; + goto lexical_error_label; + } + + } else { + if (end) + *end = ','; + sciprintf("[gfx-conf] Unexpected character '%c'\n", + *src); + goto lexical_error_label; + } + + if (!final) { + *end = ','; + src = end + 1; + } + } + + if (wildcard) { + pattern_list_free(*destp); + *destp = NULL; + } + + return 0; + + lexical_error_label: + sciprintf("[gfx-conf] Lexical error in pattern at offset %d\n", + src - src_orig); + return 1; +} + +static int +extract_mod_rule(char *src, gfx_res_mod_t *rule) +{ + char *orig_src = src; + char *endp; + float f[3]; + int i; + + rule->type = GFX_RES_MULTIPLY_FIXED; + + if (isdigit(*src) || *src == '.') { + f[0] = f[1] = f[2] = strtod(src, &endp); + + if (*endp) + goto mod_error_label; + } else if (*src == '(') { + i = 0; + ++src; + + do { + while (*src && isblank(*src)) + ++src; + if (!*src || !(isdigit(*src) || *src == '.')) { + sciprintf("[gfx-conf] Unexpected character '%c'\n", + *src); + goto mod_error_label; + } + f[i++] = strtod(src, &endp); + + src = endp; + + while (*src && isblank(*src)) + ++src; + + if ((i == 3) && *src != ')') { + sciprintf("[gfx-conf] Error: Expected ')' at end of modification rule\n"); + goto mod_error_label; + } else if (i<3 && !isdigit(*src) && *src != '.' && *src != ',') { + sciprintf("[gfx-conf] Error: Expected ',' as separator in modification rule, not '%c'\n", + *src); + goto mod_error_label; + } + ++src; + } while (i < 3); + + if (*src) { + sciprintf("[gfx-conf] Error: Trailing garbage after modification rule\n"); + goto mod_error_label; + } + + } else + goto mod_error_label; + + for (i = 0; i < 3; i++) { + int v = (int)(f[i] * 16.0); + rule->mod.factor[i] = (v > 255) ? 255 : v; + } + + return 0; + mod_error_label: + sciprintf("[gfx-conf] Ill-formed modification rule '%s'\n", + orig_src); + return 1; +} + + +extern gfx_pixmap_color_t gfx_sci0_image_colors[][GFX_SCI0_IMAGE_COLORS_NR]; + +#define PREDEFINED_PALETTES_NR 4 +static int +extract_assign_rule(char *src, gfx_res_assign_t *rule) +{ + /*char *orig_src = src;*/ + struct { + const char *name; + int colors_nr; + gfx_pixmap_color_t *colors; + } predefined_palettes[PREDEFINED_PALETTES_NR] = { + {"default", 16, (gfx_pixmap_color_t *) &(gfx_sci0_image_colors[0])}, + {"amiga", 16, (gfx_pixmap_color_t *) &(gfx_sci0_image_colors[1])}, + {"gray", 16, (gfx_pixmap_color_t *) &(gfx_sci0_image_colors[2])}, + {"grey", 16, (gfx_pixmap_color_t *) &(gfx_sci0_image_colors[2])}, + }; + int i; + + rule->type = GFX_RES_ASSIGN_TYPE_PALETTE; + + for (i = 0; i < PREDEFINED_PALETTES_NR; i++) + if (!strcmp(src, predefined_palettes[i].name)) { + rule->assign.palette.colors_nr = + predefined_palettes[i].colors_nr; + rule->assign.palette.colors = + predefined_palettes[i].colors; + return 0; + } + + sciprintf("[gfx-conf] Unknown palette '%s'\n", src); + return 1; + /* + assign_error_label: + sciprintf("[gfx-conf] Ill-formed assignment rule '%s'\n", + orig_src); + return 1; + */ +} + +#define CLASSES_NR 3 +int +gfx_update_conf(gfx_options_t *options, char *str) +{ + int total_patterns; + int mod = 0; /* Modifier or assignment rule? */ + char *orig_str = str; + char *sem_end; + + int fields_nr; /* Number of fields a restriction is possible by. + ** cursors:1, pics:2, views:3. */ + struct { + const char *class_name; + int class_id; + int fields_nr; + } classes[CLASSES_NR] = { + {"view", GFX_RESOURCE_TYPE_VIEW, 3}, + {"pic", GFX_RESOURCE_TYPE_PIC, 2}, + {"cursor", GFX_RESOURCE_TYPE_CURSOR, 1}, + }; + gfx_res_conf_t *conf = (gfx_res_conf_t*)sci_malloc(sizeof(gfx_res_conf_t)); + gfx_res_pattern_list_t *patterns = NULL; + gfx_res_pattern_list_t *loops = NULL; + gfx_res_pattern_list_t *cels = NULL; + gfx_res_pattern_list_t **fields[3] = { + &patterns, &loops, &cels + }; + int i; + int fieldcnt; + const char *pat_name_str; + + /* Extract pattern(s) */ + while (*str && isblank(*str)) + ++str; + + fields_nr = -1; + for (i = 0; i < CLASSES_NR; i++) { + int len = strlen(classes[i].class_name); + + if (!strncmp(str, classes[i].class_name, len)) { + pat_name_str = classes[i].class_name; + conf->type = classes[i].class_id; + fields_nr = classes[i].fields_nr; + str += len; + break; + } + } + + if (fields_nr == -1) { + sciprintf("[gfx-conf] Unexpected pattern class: Expected one of 'view', 'pic', 'cursor'\n"); + goto failure_label; + } + + fieldcnt = 0; + do { + while (*str && isblank(*str)) + ++str; + + if (!*str) + goto unexpected_end; + + if (*str == '=' + || *str == '*') + break; + + if (*str == '(') { + char *end = strchr(str, ')'); + + if (fieldcnt >= fields_nr) { + sciprintf("[gfx-conf] Error: Patterns of class '%s' may only be" + " constrained by %d arguments\n", + pat_name_str, fields_nr); + goto failure_label; + } + + if (!end) { + sciprintf("[gfx-conf] Unmatched parentheses at offset %d\n", + str - orig_str); + goto failure_label; + } + *end = 0; + + if (extract_pattern(fields[fieldcnt++], + str + 1, + str + 1 - orig_str)) + goto failure_label; + + *end = ')'; + str = end + 1; + + continue; + } + + sciprintf("[gfx-conf] Lexical error in pattern at offset %d: Unexpected '%c'\n", + str - orig_str, *str); + goto failure_label; + } while (1); + + + /* Flatten patterns */ + conf->patterns = NULL; + total_patterns = conf->patterns_nr = pattern_list_len(patterns); + total_patterns += (conf->loops_nr = pattern_list_len(loops)); + total_patterns += (conf->cels_nr = pattern_list_len(cels)); + + conf->patterns = (gfx_res_pattern_t*)sci_malloc(1 + (sizeof(gfx_res_pattern_t) * total_patterns)); + pattern_list_flatten(conf->patterns, patterns); + pattern_list_flatten(conf->patterns + conf->patterns_nr, loops); + pattern_list_flatten(conf->patterns + conf->patterns_nr + conf->loops_nr, cels); + + pattern_list_free(patterns); + patterns = NULL; + pattern_list_free(loops); + loops = NULL; + pattern_list_free(cels); + cels = NULL; + + /* Parse remainder */ + if (*str == '*') { + mod = 1; + ++str; + } + + if (*str != '=') { + sciprintf("[gfx-conf] Expected '='\n"); + goto failure_label; + } + + do { ++str; } + while (*str && isblank(*str)); + + sem_end = strchr(str, ';'); + if (!sem_end) { + sciprintf("[gfx-conf] Expected ';' at end of rule\n"); + goto failure_label; + } + do { *sem_end-- = 0; } + while (sem_end >= str + && isblank(*sem_end)); + + if (mod) { + if (extract_mod_rule(str, &conf->conf.mod)) + goto failure_label; + } else { + if (extract_assign_rule(str, &conf->conf.assign)) + goto failure_label; + } + + /* Write back into options */ + if (mod) { + conf->next = options->res_conf.mod[conf->type]; + options->res_conf.mod[conf->type] = conf; + } else { + conf->next = options->res_conf.assign[conf->type]; + options->res_conf.assign[conf->type] = conf; + } + + return 0; + + /* Error handling */ +unexpected_end: + sciprintf("[gfx-conf] Unexpected end of pattern encountered\n"); + failure_label: + sciprintf("[gfx-conf] Error occured in: '%s'\n", orig_str); + pattern_list_free(patterns); + pattern_list_free(loops); + pattern_list_free(cels); + if (conf->patterns) + free(conf->patterns); + free(conf); + return 1; +} + +static inline int +matches_patternlist(gfx_res_pattern_t *patterns, int nr, int val) +{ + int i; + for (i = 0; i < nr; i++) + if (patterns[i].min <= val + && patterns[i].max >= val) + return 1; + + return 0; +} + +#ifdef DEBUG +static void +print_pattern(gfx_res_pattern_t *pat) +{ + fprintf(stderr, "[%d..%d]", + pat->min, pat->max); +} +#endif + +static inline int +resource_matches_patternlists(gfx_res_conf_t *conf, + int type, int nr, int loop, int cel) +{ + int loc; +#ifdef DEBUG + int i; + fprintf(stderr, "[DEBUG:gfx-res] Trying to match against %d/%d/%d choices\n", + conf->patterns_nr, conf->loops_nr, conf->cels_nr); + for (i = 0; i < conf->patterns_nr; i++) { + fprintf(stderr, "[DEBUG:gfx-res] Pat #%d: ", i); + print_pattern(conf->patterns + i); + fprintf(stderr, "\n"); + } + loc = conf->patterns_nr; + for (i = 0; i < conf->loops_nr; i++) { + fprintf(stderr, "[DEBUG:gfx-res] Loop #%d: ", i); + print_pattern(conf->patterns + i + loc); + fprintf(stderr, "\n"); + } + loc += conf->loops_nr; + for (i = 0; i < conf->cels_nr; i++) { + fprintf(stderr, "[DEBUG:gfx-res] Cel #%d: ", i); + print_pattern(conf->patterns + i + loc); + fprintf(stderr, "\n"); + } +#endif + if (conf->patterns_nr && + !matches_patternlist(conf->patterns, + conf->patterns_nr, + nr)) + return 0; + + if (type == GFX_RESOURCE_TYPE_CURSOR) + return 1; + + /* Otherwise, we must match at least the loop (pic) + ** and, for views, the cel as well */ + loc = conf->patterns_nr; + if (conf->loops_nr && + !matches_patternlist(conf->patterns + loc, + conf->loops_nr, + loop)) + return 0; + + if (type != GFX_RESOURCE_TYPE_VIEW) + return 1; + + loc += conf->loops_nr; + + if (!conf->cels_nr) + return 1; + + return matches_patternlist(conf->patterns + loc, + conf->cels_nr, + cel); +} + +static inline gfx_res_conf_t * +find_match(gfx_res_conf_t *conflist, + int type, int nr, int loop, int cel) +{ + while (conflist) { + if (resource_matches_patternlists(conflist, + type, nr, loop, cel)) { +#ifdef DEBUG + fprintf(stderr, "[DEBUG:gfx-res] Found match!\n"); +#endif + return conflist; + } + + conflist = conflist->next; + } + return NULL; +} + +void +apply_assign(gfx_res_assign_t *conf, gfx_pixmap_t *pxm) +{ + /* Has a dynamically allocated palette? Must clean up */ + if (!(pxm->flags & GFX_PIXMAP_FLAG_EXTERNAL_PALETTE)) { + if (pxm->colors) + free(pxm->colors); + pxm->flags |= GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + } + + pxm->colors_nr = conf->assign.palette.colors_nr; + pxm->colors = conf->assign.palette.colors; +} + +void +apply_mod(gfx_res_mod_t *mod, gfx_pixmap_t *pxm) +{ + gfx_pixmap_color_t *pal = pxm->colors; + int i, pal_size = pxm->colors_nr; + + /* Does not have a dynamically allocated palette? Must dup current one */ + if (pxm->flags & GFX_PIXMAP_FLAG_EXTERNAL_PALETTE) { + int size = sizeof(gfx_pixmap_color_t) * pal_size; + pxm->colors = (gfx_pixmap_color_t*)sci_malloc(size); + memcpy(pxm->colors, pal, size); + pal = pxm->colors; + pxm->flags &= ~GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + /* Flag for later deallocation */ + } + + switch (mod->type) { + + case GFX_RES_MULTIPLY_FIXED: { + for (i = 0; i < pal_size; i++) { + int v; + +#define UPDATE_COL(nm, idx) \ + v = pal[i].nm; \ + v *= mod->mod.factor[idx]; \ + v >>= 4; \ + pal[i].nm = (v > 255)? 255 : v; + + UPDATE_COL(r, 0); + UPDATE_COL(g, 1); + UPDATE_COL(b, 2); +#undef UPDATE_COL + } + break; + } + + default: + GFXERROR("Using unexpected visual resource modifier %d\n", mod->type); + } +} + +int +gfx_get_res_config(gfx_options_t *options, gfx_pixmap_t *pxm) +{ + int restype = GFXR_RES_TYPE(pxm->ID); + int nr = GFXR_RES_NR(pxm->ID); + int loop = pxm->loop; + int cel = pxm->cel; + + gfx_res_conf_t *conf; + +#ifdef DEBUG + fprintf(stderr, "[DEBUG:gfx-res] Trying to conf %d/%d/%d/%d (ID=%d)\n", + restype, nr, loop, cel, pxm->ID); +#endif + + if (pxm->ID < 0 || restype < 0 || restype >= GFX_RESOURCE_TYPES_NR) + return 1; /* Not appropriate */ + + conf = find_match(options->res_conf.assign[restype], + restype, nr, loop, cel); + + if (conf) + apply_assign(&(conf->conf.assign), pxm); + + conf = options->res_conf.mod[restype]; + while (conf) { + conf = find_match(conf, + restype, nr, loop, cel); + if (conf) { + apply_mod(&(conf->conf.mod), pxm); + conf = conf->next; + } + } + fflush(NULL); + + return 0; +} diff --git a/engines/sci/gfx/gfx_resource.c b/engines/sci/gfx/gfx_resource.c new file mode 100644 index 0000000000..6ac7567d76 --- /dev/null +++ b/engines/sci/gfx/gfx_resource.c @@ -0,0 +1,434 @@ +/*************************************************************************** + gfx_resource.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include +#include + +gfx_mode_t mode_1x1_color_index = { /* Fake 1x1 mode */ + /* xfact */ 1, /* yfact */ 1, + /* bytespp */ 1, + /* flags */ 0, + /* palette */ NULL, + + /* color masks */ 0, 0, 0, 0, + /* color shifts */ 0, 0, 0, 0 +}; + + +static void +gfxr_free_loop(gfx_driver_t *driver, gfxr_loop_t *loop) +{ + int i; + + if (loop->cels) { + for (i = 0; i < loop->cels_nr; i++) + if (loop->cels[i]) + gfx_free_pixmap(driver, loop->cels[i]); + + free(loop->cels); + } +} + +void +gfxr_free_view(gfx_driver_t *driver, gfxr_view_t *view) +{ + int i; + + if (view->colors && !(view->flags & GFX_PIXMAP_FLAG_EXTERNAL_PALETTE)) + free(view->colors); + + if (view->loops) { + for (i = 0; i < view->loops_nr; i++) + gfxr_free_loop(driver, view->loops + i); + + free(view->loops); + } + free(view); +} + + +static void +pixmap_endianness_reverse_2_simple(byte *data, int area) +{ + int c; + for (c = 0; c < area; c++) { + byte val = *data; + *data = data[1]; + data[1] = val; + + data += 2; + } +} + +static void +pixmap_endianness_reverse_2(byte *data, int area) +{ + int c; + int sl = sizeof(unsigned long); + + for (c = 0; c < (area & ~(sl-1)); c += (sl>>1)) { + unsigned long temp; + + memcpy(&temp, data, sl); + + /* The next line will give warnings on 32 bit archs, but + ** that's OK. */ +#if SIZEOF_LONG < 8 + temp = 0; +#else + temp = ((temp & 0xff00ff00ff00ff00l) >> 8) + | ((temp & 0x00ff00ff00ff00ffl) << 8); +#endif /* SIZEOF_INT < 8 */ + + memcpy(data, &temp, sl); + + data += sl; + } + + pixmap_endianness_reverse_2_simple(data, area & (sl-1)); +} + +static void +pixmap_endianness_reverse_3_simple(byte *data, int area) +{ + int c; + for (c = 0; c < area; c++) { + byte val0 = data[0]; + + data[0] = data[2]; + data[2] = val0; + + data += 3; + } +} + +static void +pixmap_endianness_reverse_4_simple(byte *data, int area) +{ + int c; + for (c = 0; c < area; c++) { + byte val0 = data[0]; + byte val1 = data[1]; + + data[0] = data[3]; + data[3] = val0; + + data[1] = data[2]; + data[2] = val1; + + data += 4; + } +} + +static void +pixmap_endianness_reverse_4(byte *data, int area) +{ + int c; + int sl = sizeof(unsigned long); + + for (c = 0; c < (area & ~(sl-1)); c += (sl>>2)) { + unsigned long temp; + + memcpy(&temp, data, sl); + + /* The next lines will give warnings on 32 bit archs, but + ** that's OK. */ +#if SIZEOF_LONG < 8 + temp = 0l; +#else + temp = ((temp & 0xffff0000ffff0000l) >> 16) + | ((temp & 0x0000ffff0000ffffl) << 16); + temp = ((temp & 0xff00ff00ff00ff00l) >> 8) + | ((temp & 0x00ff00ff00ff00ffl) << 8); +#endif /* SIZEOF_LONG < 8 */ + + memcpy(data, &temp, sl); + + data += sl; + } + + pixmap_endianness_reverse_4_simple(data, area & (sl-1)); +} + +gfx_pixmap_t * +gfxr_endianness_adjust(gfx_pixmap_t *pixmap, gfx_mode_t *mode) +{ + int bytespp; + byte *data; + + if (!pixmap || !pixmap->data || !mode) { + GFXERROR("gfxr_endianness_adjust(): Invoked with invalid values\n"); + BREAKPOINT(); + return NULL; + } + + if (!(mode->flags & GFX_MODE_FLAG_REVERSE_ENDIAN)) + return pixmap; + + bytespp = mode->bytespp; + + data = pixmap->data; + + switch (bytespp) { + case 1: + break; + + case 2: pixmap_endianness_reverse_2(data, pixmap->xl + * pixmap->yl); + break; + + case 3: pixmap_endianness_reverse_3_simple(data, pixmap->xl + * pixmap->yl); + break; + + case 4: pixmap_endianness_reverse_4(data, pixmap->xl * pixmap->yl); + break; + + default: fprintf(stderr,"gfxr_endianness_adjust(): Cannot adjust endianness for %d bytespp!\n", bytespp); + return NULL; + } + + return pixmap; +} + + +/* Now construct the pixmap scaling functions */ +#define EXTRA_BYTE_OFFSET 0 +#define SIZETYPE guint8 +#define FUNCNAME _gfx_xlate_pixmap_unfiltered_1 +#define FUNCNAME_LINEAR _gfx_xlate_pixmap_linear_1 +#define FUNCNAME_TRILINEAR _gfx_xlate_pixmap_trilinear_1 +#define COPY_BYTES 1 +#include "gfx_pixmap_scale.c" +#undef COPY_BYTES + +#define SIZETYPE guint16 +#define FUNCNAME _gfx_xlate_pixmap_unfiltered_2 +#define FUNCNAME_LINEAR _gfx_xlate_pixmap_linear_2 +#define FUNCNAME_TRILINEAR _gfx_xlate_pixmap_trilinear_2 +#define COPY_BYTES 2 +#include "gfx_pixmap_scale.c" +#undef COPY_BYTES + +#ifdef WORDS_BIGENDIAN +# undef EXTRA_BYTE_OFFSET +# define EXTRA_BYTE_OFFSET 1 +#endif /* WORDS_BIGENDIAN */ +#define SIZETYPE guint32 +#define FUNCNAME _gfx_xlate_pixmap_unfiltered_3 +#define FUNCNAME_LINEAR _gfx_xlate_pixmap_linear_3 +#define FUNCNAME_TRILINEAR _gfx_xlate_pixmap_trilinear_3 +#define COPY_BYTES 3 +#include "gfx_pixmap_scale.c" +#undef COPY_BYTES +#ifdef WORDS_BIGENDIAN +# undef EXTRA_BYTE_OFFSET +# define EXTRA_BYTE_OFFSET 0 +#endif /* WORDS_BIGENDIAN */ + +#define SIZETYPE guint32 +#define FUNCNAME _gfx_xlate_pixmap_unfiltered_4 +#define FUNCNAME_LINEAR _gfx_xlate_pixmap_linear_4 +#define FUNCNAME_TRILINEAR _gfx_xlate_pixmap_trilinear_4 +#define COPY_BYTES 4 +#include "gfx_pixmap_scale.c" +#undef COPY_BYTES +#undef EXTRA_BYTE_OFFSET +#undef SIZETYPE + +static inline void +_gfx_xlate_pixmap_unfiltered(gfx_mode_t *mode, gfx_pixmap_t *pxm, int scale) +{ + switch (mode->bytespp) { + + case 1:_gfx_xlate_pixmap_unfiltered_1(mode, pxm, scale); + break; + + case 2:_gfx_xlate_pixmap_unfiltered_2(mode, pxm, scale); + break; + + case 3:_gfx_xlate_pixmap_unfiltered_3(mode, pxm, scale); + break; + + case 4:_gfx_xlate_pixmap_unfiltered_4(mode, pxm, scale); + break; + + default: + GFXERROR("Invalid mode->bytespp=%d\n", mode->bytespp); + + } + + if (pxm->flags & GFX_PIXMAP_FLAG_SCALED_INDEX) { + pxm->xl = pxm->index_xl; + pxm->yl = pxm->index_yl; + } else { + pxm->xl = pxm->index_xl * mode->xfact; + pxm->yl = pxm->index_yl * mode->yfact; + } +} + +static inline void +_gfx_xlate_pixmap_linear(gfx_mode_t *mode, gfx_pixmap_t *pxm, int scale) +{ + if (mode->palette || !scale) { /* fall back to unfiltered */ + _gfx_xlate_pixmap_unfiltered(mode, pxm, scale); + return; + } + + pxm->xl = pxm->index_xl * mode->xfact; + pxm->yl = pxm->index_yl * mode->yfact; + + switch (mode->bytespp) { + + case 1:_gfx_xlate_pixmap_linear_1(mode, pxm, scale); + break; + + case 2:_gfx_xlate_pixmap_linear_2(mode, pxm, scale); + break; + + case 3:_gfx_xlate_pixmap_linear_3(mode, pxm, scale); + break; + + case 4:_gfx_xlate_pixmap_linear_4(mode, pxm, scale); + break; + + default: + GFXERROR("Invalid mode->bytespp=%d\n", mode->bytespp); + + } + +} + +static inline void +_gfx_xlate_pixmap_trilinear(gfx_mode_t *mode, gfx_pixmap_t *pxm, int scale) +{ + if (mode->palette || !scale) { /* fall back to unfiltered */ + _gfx_xlate_pixmap_unfiltered(mode, pxm, scale); + return; + } + + pxm->xl = pxm->index_xl * mode->xfact; + pxm->yl = pxm->index_yl * mode->yfact; + + switch (mode->bytespp) { + + case 1:_gfx_xlate_pixmap_trilinear_1(mode, pxm, scale); + break; + + case 2:_gfx_xlate_pixmap_trilinear_2(mode, pxm, scale); + break; + + case 3:_gfx_xlate_pixmap_trilinear_3(mode, pxm, scale); + break; + + case 4:_gfx_xlate_pixmap_trilinear_4(mode, pxm, scale); + break; + + default: + GFXERROR("Invalid mode->bytespp=%d\n", mode->bytespp); + + } + +} + +void +gfx_xlate_pixmap(gfx_pixmap_t *pxm, gfx_mode_t *mode, gfx_xlate_filter_t filter) +{ + int was_allocated = 0; + + if (mode->palette + && !(pxm->flags & GFX_PIXMAP_FLAG_PALETTE_ALLOCATED)) { + int i; + + for (i = 0; i < pxm->colors_nr; i++) { + if (gfx_alloc_color(mode->palette, pxm->colors + i) < 0) { + GFXWARN("Failed to allocate color %d/%d in pixmap (color %02x/%02x/%02x)!\n", + i, pxm->colors_nr, pxm->colors[i].r, pxm->colors[i].g, pxm->colors[i].b); + pxm->colors[i].global_index = 0; + } + /* + GFXDEBUG("alloc(%02x/%02x/%02x) -> %d\n", pxm->colors[i].r,pxm->colors[i].g,pxm->colors[i].b,pxm->colors[i].global_index); + */ + } + + pxm->flags |= GFX_PIXMAP_FLAG_PALETTE_ALLOCATED; + } + + + if (!pxm->data) { + pxm->data = (byte*)sci_malloc(mode->xfact * mode->yfact * pxm->index_xl * pxm->index_yl * mode->bytespp + 1); + /* +1: Eases coying on BE machines in 24 bpp packed mode */ + /* Assume that memory, if allocated already, will be sufficient */ + + /* Allocate alpha map */ + if (!mode->alpha_mask && pxm->colors_nr < GFX_PIC_COLORS) + pxm->alpha_map = (byte*)sci_malloc(mode->xfact * mode->yfact * pxm->index_xl * pxm->index_yl + 1); + } else + was_allocated = 1; + + switch (filter) { + + case GFX_XLATE_FILTER_NONE: _gfx_xlate_pixmap_unfiltered(mode, pxm, !(pxm->flags & GFX_PIXMAP_FLAG_SCALED_INDEX)); + break; + + case GFX_XLATE_FILTER_LINEAR: _gfx_xlate_pixmap_linear(mode, pxm, !(pxm->flags & GFX_PIXMAP_FLAG_SCALED_INDEX)); + break; + + case GFX_XLATE_FILTER_TRILINEAR: _gfx_xlate_pixmap_trilinear(mode, pxm, !(pxm->flags & GFX_PIXMAP_FLAG_SCALED_INDEX)); + break; + + default: + GFXERROR("Attempt to filter pixmap %04x in invalid mode #%d\n", pxm->ID, filter); + + if (!was_allocated) { + if (!mode->alpha_mask && pxm->colors_nr < GFX_PIC_COLORS) + free(pxm->alpha_map); + free(pxm->data); + } + } +} + + +void +gfxr_free_pic(gfx_driver_t *driver, gfxr_pic_t *pic) +{ + gfx_free_pixmap(driver, pic->visual_map); + gfx_free_pixmap(driver, pic->priority_map); + gfx_free_pixmap(driver, pic->control_map); + pic->visual_map = NULL; + pic->priority_map = NULL; + pic->control_map = NULL; + if (pic->internal) + free(pic->internal); + pic->internal = NULL; + if (pic->undithered_buffer) + free(pic->undithered_buffer); + pic->undithered_buffer = 0; + free(pic); +} + diff --git a/engines/sci/gfx/gfx_support.c b/engines/sci/gfx/gfx_support.c new file mode 100644 index 0000000000..8edf9b3dff --- /dev/null +++ b/engines/sci/gfx/gfx_support.c @@ -0,0 +1,450 @@ +/*************************************************************************** + gfx_support.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Graphics support functions for drivers and replacements for driver functions +** for use with the graphical state manager +*/ + +#include +#include + +#ifdef HAVE_ALPHA_EV6_SUPPORT +int axp_have_mvi = 0; +#endif + +int gfx_crossblit_alpha_threshold = 128; + +#define DRAWLINE_FUNC _gfx_draw_line_buffer_1 +#define PIXELWIDTH 1 +#include "gfx_line.c" +#undef PIXELWIDTH +#undef DRAWLINE_FUNC + +#define DRAWLINE_FUNC _gfx_draw_line_buffer_2 +#define PIXELWIDTH 2 +#include "gfx_line.c" +#undef PIXELWIDTH +#undef DRAWLINE_FUNC + +#define DRAWLINE_FUNC _gfx_draw_line_buffer_3 +#define PIXELWIDTH 3 +#include "gfx_line.c" +#undef PIXELWIDTH +#undef DRAWLINE_FUNC + +#define DRAWLINE_FUNC _gfx_draw_line_buffer_4 +#define PIXELWIDTH 4 +#include "gfx_line.c" +#undef PIXELWIDTH +#undef DRAWLINE_FUNC + +inline void +gfx_draw_line_buffer(byte *buffer, int linewidth, int pixelwidth, point_t start, point_t end, unsigned int color) +{ + switch (pixelwidth) { + + case 1: + _gfx_draw_line_buffer_1(buffer, linewidth, start, end, color); + return; + + case 2: + _gfx_draw_line_buffer_2(buffer, linewidth, start, end, color); + return; + + case 3: + _gfx_draw_line_buffer_3(buffer, linewidth, start, end, color); + return; + + case 4: + _gfx_draw_line_buffer_4(buffer, linewidth, start, end, color); + return; + + default: + GFXERROR("pixelwidth=%d not supported!\n", pixelwidth); + return; + + } +} + + + + +void +gfx_draw_line_pixmap_i(gfx_pixmap_t *pxm, point_t start, point_t end, int color) +{ + gfx_draw_line_buffer(pxm->index_data, pxm->index_xl, 1, start, end, color); +} + + + + +void +gfx_draw_box_buffer(byte *buffer, int linewidth, rect_t zone, int color) +{ + byte *dest = buffer + zone.x + (linewidth * zone.y); + int i; + + if (zone.xl <= 0 || zone.yl <= 0) + return; + + for (i = 0; i < zone.yl; i++) { + memset(dest, color, zone.xl); + dest += linewidth; + } +} + + +void +gfx_draw_box_pixmap_i(gfx_pixmap_t *pxm, rect_t box, int color) +{ + gfx_clip_box_basic(&box, pxm->index_xl - 1, pxm->index_yl - 1); + + gfx_draw_box_buffer(pxm->index_data, pxm->index_xl, box, color); +} + + +/* Import various crossblit functions */ +#undef USE_PRIORITY +#undef FUNCTION_NAME +#undef BYTESPP + +# define FUNCTION_NAME _gfx_crossblit_8 +# define BYTESPP 1 +# include "gfx_crossblit.c" + +# undef FUNCTION_NAME +# undef BYTESPP +# define FUNCTION_NAME _gfx_crossblit_16 +# define BYTESPP 2 +# include "gfx_crossblit.c" + +# undef FUNCTION_NAME +# undef BYTESPP +# define FUNCTION_NAME _gfx_crossblit_24 +# define BYTESPP 3 +# include "gfx_crossblit.c" + +# undef FUNCTION_NAME +# undef BYTESPP +# define FUNCTION_NAME _gfx_crossblit_32 +# define BYTESPP 4 +# include "gfx_crossblit.c" + +#define USE_PRIORITY + +# undef FUNCTION_NAME +# undef BYTESPP +# define FUNCTION_NAME _gfx_crossblit_8_P +# define BYTESPP 1 +# include "gfx_crossblit.c" + +# undef FUNCTION_NAME +# undef BYTESPP +# define FUNCTION_NAME _gfx_crossblit_16_P +# define BYTESPP 2 +# include "gfx_crossblit.c" + +# undef FUNCTION_NAME +# undef BYTESPP +# define FUNCTION_NAME _gfx_crossblit_24_P +# define BYTESPP 3 +# include "gfx_crossblit.c" + +# undef FUNCTION_NAME +# undef BYTESPP +# define FUNCTION_NAME _gfx_crossblit_32_P +# define BYTESPP 4 +# include "gfx_crossblit.c" + +#undef USE_PRIORITY +#undef FUNCTION_NAME +#undef BYTESPP + +/* Reverse alpha versions */ +#undef USE_PRIORITY +#undef FUNCTION_NAME +#undef BYTESPP +#undef REVERSE_ALPHA + +#define REVERSE_ALPHA +# define FUNCTION_NAME _gfx_crossblit_8_RA +# define BYTESPP 1 +# include "gfx_crossblit.c" + +# undef FUNCTION_NAME +# undef BYTESPP +# define FUNCTION_NAME _gfx_crossblit_16_RA +# define BYTESPP 2 +# include "gfx_crossblit.c" + +# undef FUNCTION_NAME +# undef BYTESPP +# define FUNCTION_NAME _gfx_crossblit_24_RA +# define BYTESPP 3 +# include "gfx_crossblit.c" + +# undef FUNCTION_NAME +# undef BYTESPP +# define FUNCTION_NAME _gfx_crossblit_32_RA +# define BYTESPP 4 +# include "gfx_crossblit.c" + +#define USE_PRIORITY + +# undef FUNCTION_NAME +# undef BYTESPP +# define FUNCTION_NAME _gfx_crossblit_8_P_RA +# define BYTESPP 1 +# include "gfx_crossblit.c" + +# undef FUNCTION_NAME +# undef BYTESPP +# define FUNCTION_NAME _gfx_crossblit_16_P_RA +# define BYTESPP 2 +# include "gfx_crossblit.c" + +# undef FUNCTION_NAME +# undef BYTESPP +# define FUNCTION_NAME _gfx_crossblit_24_P_RA +# define BYTESPP 3 +# include "gfx_crossblit.c" + +# undef FUNCTION_NAME +# undef BYTESPP +# define FUNCTION_NAME _gfx_crossblit_32_P_RA +# define BYTESPP 4 +# include "gfx_crossblit.c" + +#undef USE_PRIORITY +#undef FUNCTION_NAME +#undef BYTESPP +#undef REVERSE_ALPHA + +static void (*crossblit_fns[5])(byte *, byte *, int, int, int, int, byte *, int, int, unsigned int, unsigned int) = +{ NULL, + _gfx_crossblit_8, + _gfx_crossblit_16, + _gfx_crossblit_24, + _gfx_crossblit_32 }; + +static void (*crossblit_fns_P[5])(byte *, byte *, int, int, int, int, byte *, int, int, unsigned int, unsigned int, byte *, int, int, int) = +{ NULL, + _gfx_crossblit_8_P, + _gfx_crossblit_16_P, + _gfx_crossblit_24_P, + _gfx_crossblit_32_P }; + +static void (*crossblit_fns_RA[5])(byte *, byte *, int, int, int, int, byte *, int, int, unsigned int, unsigned int) = +{ NULL, + _gfx_crossblit_8_RA, + _gfx_crossblit_16_RA, + _gfx_crossblit_24_RA, + _gfx_crossblit_32_RA }; + +static void (*crossblit_fns_P_RA[5])(byte *, byte *, int, int, int, int, byte *, int, int, unsigned int, unsigned int, byte *, int, int, int) = +{ NULL, + _gfx_crossblit_8_P_RA, + _gfx_crossblit_16_P_RA, + _gfx_crossblit_24_P_RA, + _gfx_crossblit_32_P_RA }; + + +void +_gfx_crossblit_simple(byte *dest, byte *src, int dest_line_width, int src_line_width, + int xl, int yl, int bpp) +{ + int line_width = xl * bpp; + int i; + + for (i = 0; i < yl; i++) { + memcpy(dest, src, line_width); + dest += dest_line_width; + src += src_line_width; + } +} + +int +gfx_crossblit_pixmap(gfx_mode_t *mode, gfx_pixmap_t *pxm, int priority, + rect_t src_coords, + rect_t dest_coords, byte *dest, int dest_line_width, + byte *priority_dest, int priority_line_width, + int priority_skip, int flags) +{ + int maxx = 320 * mode->xfact; + int maxy = 200 * mode->yfact; + byte *src = pxm->data; + byte *alpha = pxm->alpha_map? pxm->alpha_map : pxm->data; + byte *priority_pos = priority_dest; + unsigned int alpha_mask, alpha_min; + int bpp = mode->bytespp; + int bytes_per_alpha_pixel = pxm->alpha_map? 1 : bpp; + int bytes_per_alpha_line = bytes_per_alpha_pixel * pxm->xl; + int xl = pxm->xl, yl = pxm->yl; + int xoffset = (dest_coords.x < 0)? - dest_coords.x : 0; + int yoffset = (dest_coords.y < 0)? - dest_coords.y : 0; + int revalpha = mode->flags & GFX_MODE_FLAG_REVERSE_ALPHA; + + if (src_coords.x + src_coords.xl > xl) + src_coords.xl = xl - src_coords.x; + + if (src_coords.y + src_coords.yl > yl) + src_coords.yl = yl - src_coords.y; + +/** --???-- **/ + if (src_coords.y > yl) + return GFX_OK; + if (src_coords.x > xl) + return GFX_OK; +/** --???-- **/ + + if (dest_coords.x + xl >= maxx) + xl = maxx - dest_coords.x; + if (dest_coords.y + yl >= maxy) + yl = maxy - dest_coords.y; + + xl -= xoffset; + yl -= yoffset; + + if (!pxm->data) + return GFX_ERROR; + + if (xl <= 0 || yl <= 0) + return GFX_OK; + + /* Set destination offsets */ + + /* Set x offsets */ + if (!(flags & GFX_CROSSBLIT_FLAG_DATA_IS_HOMED)) + dest += dest_coords.x * bpp; + priority_pos += dest_coords.x * priority_skip; + + /* Set y offsets */ + if (!(flags & GFX_CROSSBLIT_FLAG_DATA_IS_HOMED)) + dest += dest_coords.y * dest_line_width; + priority_pos += dest_coords.y * priority_line_width; + + /* Set source offsets */ + if (xoffset += src_coords.x) { + dest_coords.x = 0; + src += xoffset * bpp; + alpha += xoffset * bytes_per_alpha_pixel; + } + + + if (yoffset += src_coords.y) { + dest_coords.y = 0; + src += yoffset * bpp * pxm->xl; + alpha += yoffset * bytes_per_alpha_line; + } + + /* Adjust length for clip box */ + if (xl > src_coords.xl) + xl = src_coords.xl; + if (yl > src_coords.yl) + yl = src_coords.yl; + + /* now calculate alpha */ + if (pxm->alpha_map) + alpha_mask = 0xff; + else { + int shift_nr = 0; + + alpha_mask = mode->alpha_mask; + if (!alpha_mask && pxm->alpha_map) { + GFXERROR("Invalid alpha mode: both pxm->alpha_map and alpha_mask are white!\n"); + return GFX_ERROR; + } + + if (alpha_mask) { + while (!(alpha_mask & 0xff)) { + alpha_mask >>= 8; + shift_nr++; + } + alpha_mask &= 0xff; + } + +#ifdef WORDS_BIGENDIAN + alpha += (mode->bytespp) - (shift_nr + 1); +#else + alpha += shift_nr; +#endif + } + +#ifdef HAVE_ALPHA_EV6_SUPPORT + if (mode->alpha_mask && axp_have_mvi && bpp == 4) { + if (priority == GFX_NO_PRIORITY) + alpha_mvi_crossblit_32(dest, src, dest_line_width, pxm->xl * bpp, + xl, yl, NULL, 0, 0, mode->alpha_mask, 24 - mode->alpha_shift); + else + alpha_mvi_crossblit_32_P(dest, src, dest_line_width, pxm->xl * bpp, + xl, yl, NULL, 0, 0, mode->alpha_mask, 24 - mode->alpha_shift, + priority_pos, priority_line_width, priority_skip, priority); + } else { +#endif + + if (alpha_mask & 0xff) + alpha_min = ((alpha_mask * gfx_crossblit_alpha_threshold) >> 8) & alpha_mask; + else + alpha_min = ((alpha_mask >> 8) * gfx_crossblit_alpha_threshold) & alpha_mask; + + if (revalpha) + alpha_min = 255 - alpha_min; /* Since we use it for the reverse effect */ + + if (!alpha_mask) + _gfx_crossblit_simple(dest, src, dest_line_width, pxm->xl * bpp, + xl, yl, bpp); + else + + if (priority == GFX_NO_PRIORITY) { + if (bpp > 0 && bpp < 5) + ((revalpha) ? crossblit_fns_RA : crossblit_fns)[bpp](dest, src, dest_line_width, pxm->xl * bpp, + xl, yl, alpha, bytes_per_alpha_line, bytes_per_alpha_pixel, + alpha_mask, alpha_min); + else { + GFXERROR("Invalid mode->bytespp: %d\n", mode->bytespp); + return GFX_ERROR; + } + } else { /* priority */ + if (bpp > 0 && bpp < 5) + ((revalpha) ? crossblit_fns_P_RA : crossblit_fns_P)[bpp](dest, src, dest_line_width, pxm->xl * bpp, + xl, yl, alpha, bytes_per_alpha_line, bytes_per_alpha_pixel, + alpha_mask, alpha_min, priority_pos, + priority_line_width, priority_skip, priority); + else { + GFXERROR("Invalid mode->bytespp: %d\n", mode->bytespp); + return GFX_ERROR; + } + } +#ifdef HAVE_ALPHA_EV6_SUPPORT + } +#endif + + return GFX_OK; +} + + + + diff --git a/engines/sci/gfx/gfx_test.c b/engines/sci/gfx/gfx_test.c new file mode 100644 index 0000000000..2b5b8f36ec --- /dev/null +++ b/engines/sci/gfx/gfx_test.c @@ -0,0 +1,1504 @@ +/*************************************************************************** + gfx_test.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* gfx driver test and verification program */ + +#define DISABLE_SCI_MEMORY + +#ifdef _MSC_VER +# include +# include +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int +sci0_palette; + +gfx_pixmap_color_t gfx_sci0_image_colors[1][16]; + +gfx_pixmap_color_t * +gfxr_interpreter_get_static_palette(gfx_resstate_t *state, int version, int *colors_nr, void *internal) +{ + return NULL; +} + +int +sciprintf(const char *fmt, ...) +{ + va_list argp; + va_start(argp, fmt); + vprintf(fmt, argp); + va_end(argp); + return 0; +} + + +void * +memdup(void *mem, size_t size) +{ + void *r = malloc(size); + if (r) + memcpy(r, mem, size); + return r; +} + +#ifdef FREESCI_PRIMARY_RESOURCE_H_ +# include "../scicore/sci_memory.c" +#endif + +void +sci_gettime(long *seconds, long *useconds) +{ + struct timeval tv; + +#ifdef _WIN32 + timeBeginPeriod(0); +#endif + + assert(!gettimeofday(&tv, NULL)); + *seconds = time(NULL); + *useconds = tv.tv_usec; + +#ifdef _WIN32 + timeEndPeriod(0); +#endif +} + +static int xres = 1; +static int yres = 1; +static int color_mode = 1; +static char *driver = NULL; +static int set_mode = 0; +static int nowait = 0; +static int skip_intro = 0; + +#define ALL_TESTS "abcdefghijkl" +static char tests[256]; + +gfx_state_t graphics_state; +gfx_state_t *state = &graphics_state; + +gfx_options_t graphics_options; +gfx_options_t *options = &graphics_options; + + +#define CAPABILITY_IDS_NR 11 + +static struct { + int mask; + char *description; +} capability_ids[CAPABILITY_IDS_NR] = { + {GFX_CAPABILITY_SHADING, "shaded boxes"}, + {GFX_CAPABILITY_MOUSE_POINTER, "mouse pointers"}, + {GFX_CAPABILITY_COLOR_MOUSE_POINTER, "multi-color mouse pointers"}, + {GFX_CAPABILITY_PIXMAP_REGISTRY, "pixmaps must be registered"}, + {GFX_CAPABILITY_SCALEABLE_PIXMAPS, "pixmap scaling"}, + {GFX_CAPABILITY_STIPPLED_LINES, "stippled lines"}, + {GFX_CAPABILITY_MOUSE_SUPPORT, "pointing device input"}, + {GFX_CAPABILITY_POINTER_PIXMAP_REGISTRY, "pointer pixmaps must be registered"}, + {GFX_CAPABILITY_FINE_LINES, "fine lines"}, + {GFX_CAPABILITY_WINDOWED, "windowed mode active"}, + {GFX_CAPABILITY_KEYTRANSLATE, "built-in keyboard translation"} +}; + +int +init_driver(gfx_driver_t *drv) +{ + int i; + + state->driver = drv; + options->dirty_frames = GFXOP_DIRTY_FRAMES_CLUSTERS; + + if (set_mode) { + if (gfxop_init(state, xres, yres, color_mode, options, NULL)) { + printf("Custom initialization failed\n"); + return 1; + } + } else { + if (gfxop_init_default(state, options, NULL)) { + printf("Default initialization failed\n"); + return 1; + } + } + + printf("Using graphics driver %s, version %s\n", drv->name, drv->version); + printf("Graphics driver capabilities:\n"); + for (i = 0; i < CAPABILITY_IDS_NR; i++) + if (drv->capabilities & capability_ids[i].mask) + printf("\t%s\n", capability_ids[i].description); + + printf("\n"); + return 0; +} + + +/* ---------------------------------- */ +/* Functions for the resource manager */ +/* ---------------------------------- */ + +int multicolored_pointers = 1; /* Whether to test multicolored pointer support */ + +#define TEST_PICS_NR 2 +#define TEST_VIEWS_NR 1 +#define TEST_FONTS_NR 1 +#define TEST_CURSORS_NR 2 + +int test_pics[TEST_PICS_NR] = {0, 1}; +int test_views[TEST_VIEWS_NR] = {0}; +int test_fonts[TEST_FONTS_NR] = {0}; +int test_cursors[TEST_CURSORS_NR] = {0, 1}; + +int +gfxr_interpreter_options_hash(gfx_resource_type_t type, int version, gfx_options_t *options, void *internal, int palette) +{ + return 0; +} + + +int * +arrdup(int *src, int count) +{ + int *retval = sci_malloc(sizeof(int) * count); + memcpy(retval, src, sizeof(int) * count); + return retval; +} + +int * +gfxr_interpreter_get_resources(gfx_resstate_t *resstate, gfx_resource_type_t type, + int version, int *entries_nr, void *internal) +{ + switch (type) { + + case GFX_RESOURCE_TYPE_VIEW: + *entries_nr = TEST_VIEWS_NR; + return arrdup(test_views, TEST_VIEWS_NR); + + case GFX_RESOURCE_TYPE_PIC: + *entries_nr = TEST_PICS_NR; + return arrdup(test_pics, TEST_PICS_NR); + + case GFX_RESOURCE_TYPE_FONT: + *entries_nr = TEST_FONTS_NR; + return arrdup(test_fonts, TEST_FONTS_NR); + + case GFX_RESOURCE_TYPE_CURSOR: + *entries_nr = TEST_CURSORS_NR; + return arrdup(test_cursors, TEST_CURSORS_NR); + + default: + fprintf(stderr,"Attept to get resource list for invalid resource type %d\n", type); + return NULL; + } +} + +#define PIC_COLORS_NR 32 + +gfx_pixmap_color_t pic_colors[PIC_COLORS_NR] = { + {GFX_COLOR_INDEX_UNMAPPED, 0x0f, 0x0f, 0x0f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x1f, 0x1f, 0x1f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x2f, 0x2f, 0x2f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x3f, 0x3f, 0x3f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x4f, 0x4f, 0x4f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x5f, 0x5f, 0x5f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x6f, 0x6f, 0x6f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x7f, 0x7f, 0x7f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x8f, 0x8f, 0x8f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x9f, 0x9f, 0x9f}, + {GFX_COLOR_INDEX_UNMAPPED, 0xaf, 0xaf, 0xaf}, + {GFX_COLOR_INDEX_UNMAPPED, 0xbf, 0xbf, 0xbf}, + {GFX_COLOR_INDEX_UNMAPPED, 0xcf, 0xcf, 0xcf}, + {GFX_COLOR_INDEX_UNMAPPED, 0xdf, 0xdf, 0xdf}, + {GFX_COLOR_INDEX_UNMAPPED, 0xef, 0xef, 0xef}, + {GFX_COLOR_INDEX_UNMAPPED, 0xff, 0xff, 0xff}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x00, 0x0f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x00, 0x1f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x00, 0x2f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x00, 0x3f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x00, 0x4f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x00, 0x5f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x00, 0x6f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x00, 0x7f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x00, 0x8f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x00, 0x9f}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x00, 0xaf}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x00, 0xbf}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x00, 0xcf}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x00, 0xdf}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x00, 0xef}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x00, 0xff} +}; + +gfxr_pic_t * +gfxr_interpreter_init_pic(int version, gfx_mode_t *mode, int ID, void *internal) +{ + gfxr_pic_t *pic = sci_malloc(sizeof(gfxr_pic_t)); + + pic->mode = mode; + pic->undithered_buffer = NULL; + pic->internal = NULL; + + pic->control_map = gfx_pixmap_alloc_index_data(gfx_new_pixmap(320, 200, ID, 2, 0)); + pic->priority_map = gfx_pixmap_alloc_index_data(gfx_new_pixmap(320 * mode->xfact, + 200 * mode->yfact, ID, 1, 0)); + pic->visual_map = gfx_pixmap_alloc_index_data(gfx_new_pixmap(320, 200, ID, 0, 0)); + + pic->visual_map->colors = pic_colors; + pic->visual_map->colors_nr = PIC_COLORS_NR; + + pic->visual_map->flags = GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + pic->priority_map->flags = GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + pic->control_map->flags = GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + + pic->priority_map->colors = pic_colors; + pic->priority_map->colors_nr = PIC_COLORS_NR; + pic->control_map->colors = pic_colors; + pic->control_map->colors_nr = PIC_COLORS_NR; + + return pic; +} + + + +void +gfxr_interpreter_clear_pic(int version, gfxr_pic_t *pic, void *internal) +{ + memset(pic->visual_map->index_data, 0x00, 320 * 200); + memset(pic->priority_map->index_data, 0, 320 * pic->mode->xfact * 200 * pic->mode->yfact); + memset(pic->control_map->index_data, 0, GFXR_AUX_MAP_SIZE); + memset(pic->aux_map, 0, GFXR_AUX_MAP_SIZE); +} + + +int +gfxr_interpreter_calculate_pic(gfx_resstate_t *state, gfxr_pic_t *scaled_pic, gfxr_pic_t *unscaled_pic, + int flags, int default_palette, int nr, void *internal) +{ + gfxr_pic_t *pic = scaled_pic; + int i, x, y, pos; + int xfact = pic->mode->xfact; + int yfact = pic->mode->yfact; + + if (nr < 0 || nr > TEST_PICS_NR) + return GFX_ERROR; + + switch (nr) { + case 0: + pos = 0; + for (y = 0; y < 200; y++) { + for (x = 0; x < 32; x++) { + memset(pic->visual_map->index_data + pos, x, 5); + memset(pic->visual_map->index_data + 315 + pos - x*10, x, 5); + pos += 5; + } + pos += 160; + } + + for (y = 0; y < 200*yfact; y++) { + memset(pic->priority_map->index_data + y * 320*xfact + (100*xfact), 10, 120*xfact); + memset(pic->priority_map->index_data + y * 320*xfact + (150*xfact), 20, 20*xfact); + } + break; + + case 1: + memset(pic->visual_map->index_data + 80*320, 15, 120*320); + memset(pic->priority_map->index_data + 80*xfact * 320*yfact, 20, + 120*320*xfact*yfact); + + for (i = 40; i < 80; i++) { + int j; + + memset(pic->visual_map->index_data + i*320 + 140, 15, 80); + for (j = 0; j < pic->mode->yfact; j++) + memset(pic->priority_map->index_data + (i*yfact+j)*320*xfact + + 140*xfact , 20, 80*xfact); + } + break; + + default: + fprintf(stderr,"Attempt to reference invalid pic #%d\n", nr); + } + + printf(">> resource manager retreived pic #%d\n", nr); + return GFX_OK; +} + + +#define VIEW_COLORS_NR 16 + +gfx_pixmap_color_t view_colors[VIEW_COLORS_NR] = { + {GFX_COLOR_INDEX_UNMAPPED, 0x0f, 0x00, 0x00}, + {GFX_COLOR_INDEX_UNMAPPED, 0x2f, 0x20, 0x00}, + {GFX_COLOR_INDEX_UNMAPPED, 0x4f, 0x40, 0x00}, + {GFX_COLOR_INDEX_UNMAPPED, 0x6f, 0x60, 0x00}, + {GFX_COLOR_INDEX_UNMAPPED, 0x8f, 0x80, 0x00}, + {GFX_COLOR_INDEX_UNMAPPED, 0xaf, 0xa0, 0x00}, + {GFX_COLOR_INDEX_UNMAPPED, 0xcf, 0xc0, 0x00}, + {GFX_COLOR_INDEX_UNMAPPED, 0xef, 0xe0, 0x00}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x1f, 0x00}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x3f, 0x00}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x5f, 0x00}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x7f, 0x00}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x9f, 0x00}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0xbf, 0x00}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0xdf, 0x00}, + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0xff, 0x00} +}; + +gfxr_view_t * +gfxr_interpreter_get_view(gfx_resstate_t *state, int nr, void *internal, int palette) +{ + gfxr_view_t *view; + gfxr_loop_t *loop; + int i; + + if (nr < 0 || nr > TEST_VIEWS_NR) + return NULL; + + view = sci_malloc(sizeof(gfxr_view_t)); + view->ID = nr | 2048; + view->flags = GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + + view->colors_nr = VIEW_COLORS_NR; + view->colors = view_colors; + + view->loops_nr = 1; + view->loops = loop = sci_malloc(sizeof(gfxr_loop_t)); + + loop->cels_nr = 3; + loop->cels = sci_malloc(sizeof(gfx_pixmap_t *) * loop->cels_nr); + + for (i = 0; i < 3; i++) { + gfx_pixmap_t *pxm = gfx_pixmap_alloc_index_data(gfx_new_pixmap(16, 16, 2048 | nr, 0, i)); + int offset = (i == 1)? 8 : 0; + int x, y; + + pxm->colors_nr = VIEW_COLORS_NR; + pxm->flags = GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + pxm->colors = view_colors; + pxm->xoffset = 8; + pxm->yoffset = 8; + + for (y = 0; y < 16; y++) + for (x = 0; x < 16; x++) { + int dx = (8-x); + int dy = (8-y); + int dist = dx*dx + dy*dy; + int index = (dist * 8) / 64; + int pos = x + y*16; + + if (i == 2) { + offset = (!dx || !dy)? 8 : 0; + if (offset == 8) index <<= 1; + } + + index = 7-index; + if (index < 0) + pxm->index_data[pos] = 0xff; + else + pxm->index_data[pos] = index + offset; + } + + loop->cels[i] = pxm; + } + + printf(">> resource manager retreived view #%d\n", nr); + + return view; +} + +#define BUILTIN_CHARS_NR 256 +#define BUILTIN_CHARS_HEIGHT 8 +#define BUILTIN_CHARS_WIDTH 8 + +extern byte builtin_font[]; + +gfx_bitmap_font_t * +gfxr_interpreter_get_font(gfx_resstate_t *state, int nr, void *internal) +{ + gfx_bitmap_font_t *font; + int i; + if (nr < 0 || nr > TEST_FONTS_NR) + return NULL; + + font = sci_malloc(sizeof(gfx_bitmap_font_t)); + font->ID = nr; + font->chars_nr = BUILTIN_CHARS_NR; + font->widths = sci_malloc(sizeof(int) * BUILTIN_CHARS_NR); + for (i = 0; i < BUILTIN_CHARS_NR; i++) + font->widths[i] = BUILTIN_CHARS_WIDTH; + font->row_size = (BUILTIN_CHARS_WIDTH + 7) >> 3; + font->height = font->line_height = BUILTIN_CHARS_HEIGHT; + font->char_size = ((BUILTIN_CHARS_WIDTH + 7) >> 3) * BUILTIN_CHARS_HEIGHT; + font->data = memdup(builtin_font, font->char_size * BUILTIN_CHARS_NR); + + printf(">> resource manager retreived font #%d\n", nr); + + return font; +} + +gfx_pixmap_color_t _cursor_colors[3] = { + {GFX_COLOR_INDEX_UNMAPPED, 0, 0, 0}, + {GFX_COLOR_INDEX_UNMAPPED, 0xff, 0xff, 0xff}, + {GFX_COLOR_INDEX_UNMAPPED, 0x80, 0x80, 0x80} +}; + +gfx_pixmap_t * +gfxr_interpreter_get_cursor(gfx_resstate_t *state, int nr, void *internal) +{ + gfx_pixmap_t *cursor; + int xl, yl, x, y; + + if (nr < 0 || nr > TEST_CURSORS_NR) + return NULL; + + if (!nr) + xl = yl = 31; + else { + xl = 8; + yl = 16; + } + + + cursor = gfx_pixmap_alloc_index_data(gfx_new_pixmap(xl, yl, 1024 + nr, 0, 0)); + cursor->colors = _cursor_colors; + cursor->flags |= GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + + memset(cursor->index_data, 0xff, xl * yl); + + switch (nr) { + case 0: + cursor->colors_nr = multicolored_pointers? 3 : 2; + cursor->xoffset = 16; + cursor->yoffset = 16; + + for (x = 0; x < 31; x++) if (x != 16) { + cursor->index_data[31*x + 16] = 1; + cursor->index_data[31*16 + x] = 1; + + if (multicolored_pointers && ((x < 8) || (x > 23))) { + cursor->index_data[31*x + 14] = 0; + cursor->index_data[31*x + 15] = 2; + cursor->index_data[31*x + 17] = 2; + cursor->index_data[31*x + 18] = 0; + + cursor->index_data[31*14 + x] = 0; + cursor->index_data[31*15 + x] = 2; + cursor->index_data[31*17 + x] = 2; + cursor->index_data[31*18 + x] = 0; + } else { + cursor->index_data[31*x + 15] = 0; + cursor->index_data[31*x + 17] = 0; + + cursor->index_data[31*15 + x] = 0; + cursor->index_data[31*17 + x] = 0; + } + } + break; + + case 1: + cursor->colors_nr = 2; + cursor->xoffset = 0; + cursor->yoffset = 0; + for (y = 0; y < yl; y++) + for (x = 0; x <= (y >> 1); x++) + cursor->index_data[x + y*xl] = 1; + break; + + default: + fprintf(stderr,"Attempt to load invalid pointer %d\n", nr); + gfx_free_pixmap(state->driver, cursor); + return NULL; + } + + printf(">> resource manager retreived cursor #%d\n", nr); + + return cursor; +} + +gfx_pixmap_color_t * +gfxr_interpreter_get_palette(gfx_resstate_t *state, int version, int *colors_nr, void *internal, int nr) +{ + return NULL; +} + +int +gfxr_interpreter_needs_multicolored_pointers(int version, void *internal) +{ + return multicolored_pointers; +} + +gfx_color_t red, green, blue, dblue, white, white8, white16, white24, black, transparent; + +void +init_colors() +{ + gfxop_set_color(state, &red, 0xff, 0x00, 0x00, 0x00, -1, -1); + gfxop_set_color(state, &green, 0x00, 0xff, 0x00, 0x00, -1, -1); + gfxop_set_color(state, &blue, 0x00, 0x00, 0xff, 0x00, -1, -1); + gfxop_set_color(state, &dblue, 0x00, 0x00, 0x40, 0x00, -1, -1); + gfxop_set_color(state, &white, 0xff, 0xff, 0xff, 0x00, -1, -1); + gfxop_set_color(state, &white8, 0xff, 0xff, 0xff, 0x00, 8, -1); + gfxop_set_color(state, &white16, 0xff, 0xff, 0xff, 0x00, 16, -1); + gfxop_set_color(state, &white24, 0xff, 0xff, 0xff, 0x00, 24, -1); + gfxop_set_color(state, &black, 0x00, 0x00, 0x00, 0x00, -1, -1); + gfxop_set_color(state, &transparent, -1 , -1 , -1 , 0x00, -1, -1); +} + + +#define MESSAGE(foo) if (message(foo)) { fprintf(stderr,"Message '%s' could not be print!\n", foo); return;} +#define MESSAGE1(foo,a) { char buf[1024]; sprintf(buf,foo,a); if (message(buf)) { fprintf(stderr,"Message '%s' could not be print!\n", buf); return;}} +#define MESSAGE2(foo,a,b) { char buf[1024]; sprintf(buf,foo,a,b); if (message(buf)) { fprintf(stderr,"Message '%s' could not be print!\n", buf); return;}} +#define MESSAGE3(foo,a,b,c) { char buf[1024]; sprintf(buf,foo,a,b,c); if (message(buf)) { fprintf(stderr,"Message '%s' could not be print!\n", buf); return;}} +#define MESSAGE4(foo,a,b,c,d) { char buf[1024]; sprintf(buf,foo,a,b,c,d); if (message(buf)) { fprintf(stderr,"Message '%s' could not be print!\n", buf); return;}} + + +int +waitkey(void) +{ + int count = 100000; + sci_event_t event; + + while (count--) { + event = gfxop_get_event(state, SCI_EVT_ANY); + + if (event.type) + return 0; + + gfxop_usleep(state, 1000); + } + return 1; +} + +int +wait_specific_key(int key) +{ + int count = 20000; + sci_event_t event; + + while (count--) { + event = gfxop_get_event(state, SCI_EVT_ANY); + + if (event.type == SCI_EVT_KEYBOARD + && event.data == key) + return 0; + + gfxop_usleep(state, 1000); + } + return 1; +} + + +int +message(char *msg) +{ + gfx_text_handle_t *handle; + rect_t text_rect = gfx_rect(0, 150, 320, 50); + + + handle = gfxop_new_text(state, 0, msg, 320, ALIGN_CENTER, ALIGN_TOP, + white, white, black, 0); + + if (!handle) return 1; + + printf("-----------------------------------------\n%s\n-----------------------------------------\n", msg); + + gfxop_fill_box(state, text_rect, black); + gfxop_draw_text(state, handle, text_rect); + gfxop_free_text(state, handle); + gfxop_update(state); + return 0; +} + +void +update(void) +{ + /* gfxop_update_box(state, gfx_rect(0, 0, 320, 150)); */ + gfxop_update(state); +} + +void +explicit_clear_buffer(void) +{ + gfxop_clear_box(state, gfx_rect(0, 0, 320, 150)); + gfxop_update(state); +} + +void +clear_buffer(void) +{ + gfxop_disable_dirty_frames(state); + gfxop_clear_box(state, gfx_rect(0, 0, 320, 150)); + gfxop_enable_dirty_frames(state); +} + +void +clear(void) +{ + gfxop_fill_box(state, gfx_rect(0, 0, 320, 150), black); +} + +void +identify_event(sci_event_t event) +{ + switch(event.type) { + + case SCI_EVT_NONE: + MESSAGE("No event"); + break; + + case SCI_EVT_MOUSE_PRESS: + MESSAGE4("Mouse press at (%d,%d)\ndata/modifiers (%d/%d)", + state->pointer_pos.x, state->pointer_pos.y, event.data, event.buckybits); + break; + + case SCI_EVT_MOUSE_RELEASE: + MESSAGE4("Mouse release at (%d,%d)\ndata/modifiers (%d/%d)", + state->pointer_pos.x, state->pointer_pos.y, event.data, event.buckybits); + break; + + case SCI_EVT_KEYBOARD: + if (event.data > 127) { + MESSAGE2("Key 0x%04x\nmodifiers %04x", event.data, event.buckybits); + } else + MESSAGE3("Key '%c' (0x%02x)\nmodifiers %04x", event.data, event.data, event.buckybits); + break; + + case SCI_EVT_JOYSTICK: + MESSAGE1("Joystick direction %d", event.data); + break; + + case SCI_EVT_ERROR: + MESSAGE("Error event"); + break; + + default: MESSAGE1("Unknown event type %d!\n", event.type); + } +} + + +int +test_a(void) +{ + if (message("-- Test A --\nText display and basic input\nPlease press 'space' within 20 seconds")) + return 1; + + return (wait_specific_key(' ')); +} + + +#define TEST_LINE(x, y, xl, yl) \ + gfxop_draw_line(state, gfx_point(x, y), gfx_point((x)+(xl), (y)+(yl)), blue, GFX_LINE_MODE_FAST, GFX_LINE_STYLE_NORMAL); \ + gfxop_set_clip_zone(state, gfx_rect(140, 60, 40, 40)); \ + gfxop_draw_line(state, gfx_point(x, y), gfx_point((x)+(xl), (y)+(yl)), red, GFX_LINE_MODE_FAST, GFX_LINE_STYLE_NORMAL); \ + gfxop_set_clip_zone(state, gfx_rect(0, 0, 320, 200)); + +#define LINES_NR 19 + +int test_b_lines[LINES_NR][4] = { + {10, 10, 300, 0}, + {160, 30, 0, 100}, + {162, 20, 0, 50}, + {162, 90, 0, 50}, + {110, 80, 100, 0}, + {100, 82, 50, 0}, + {170, 82, 50, 0}, + {135, 70, 20, -20}, + {135, 90, 20, 20}, + {185, 70, -20, -20}, + {185, 90, -20, 20}, + {150, 70, -20, -10}, + {150, 70, -10, -20}, + {170, 70, 20, -10}, + {170, 70, 10, -20}, + {150, 90, -20, 10}, + {150, 90, -10, 20}, + {170, 90, 10, 20}, + {170, 90, 20, 10} +}; + +void +test_b(void) +{ + int i; + + MESSAGE("-- Test B --\nLines"); + waitkey(); + MESSAGE("Some tests will include 'fine' lines.\nNote that support for those is\noptional."); + waitkey(); + + gfxop_draw_line(state, gfx_point(30, 30), gfx_point(290, 30), red, GFX_LINE_MODE_FINE, GFX_LINE_STYLE_NORMAL); + gfxop_draw_line(state, gfx_point(30, 40), gfx_point(290, 40), blue, GFX_LINE_MODE_FAST, GFX_LINE_STYLE_NORMAL); + gfxop_draw_line(state, gfx_point(30, 50), gfx_point(290, 50), green, GFX_LINE_MODE_FINE, GFX_LINE_STYLE_STIPPLED); + gfxop_draw_line(state, gfx_point(30, 60), gfx_point(290, 60), white, GFX_LINE_MODE_FAST, GFX_LINE_STYLE_STIPPLED); + update(); + MESSAGE("B.0: horizontal lines:\nYou should now be seeing (top-down):\nred-fine, blue-normal,\ngreen-fine-stippled, white-stippled"); + waitkey(); + + clear(); + gfxop_draw_line(state, gfx_point(30, 30), gfx_point(290, 130), blue, GFX_LINE_MODE_FAST, GFX_LINE_STYLE_NORMAL); + gfxop_draw_line(state, gfx_point(30, 130), gfx_point(290, 30), blue, GFX_LINE_MODE_FAST, GFX_LINE_STYLE_NORMAL); + gfxop_set_clip_zone(state, gfx_rect(140, 60, 40, 40)); + gfxop_draw_line(state, gfx_point(30, 30), gfx_point(290, 130), red, GFX_LINE_MODE_FAST, GFX_LINE_STYLE_NORMAL); + gfxop_draw_line(state, gfx_point(30, 130), gfx_point(290, 30), red, GFX_LINE_MODE_FAST, GFX_LINE_STYLE_NORMAL); + gfxop_set_clip_zone(state, gfx_rect(0, 0, 320, 200)); + update(); + MESSAGE("B.1: line clipping:\nTwo identical pairs of lines.\nblue lines are unclipped,\nred ones are clipped."); + waitkey(); + + clear(); + for (i = 0; i < LINES_NR; i++) { + TEST_LINE(test_b_lines[i][0], test_b_lines[i][1], test_b_lines[i][2], test_b_lines[i][3]); + } + update(); + MESSAGE1("B.2: line clipping:\n%d lines.", LINES_NR); + waitkey(); + + clear(); + gfxop_draw_rectangle(state, gfx_rect(30, 30, 260, 100), red, GFX_LINE_MODE_FAST, GFX_LINE_STYLE_NORMAL); + gfxop_draw_rectangle(state, gfx_rect(40, 40, 240, 80), red, GFX_LINE_MODE_FAST, GFX_LINE_STYLE_STIPPLED); + update(); + MESSAGE("B.3: Rectangles:\nNormal rectangle (outside)\nStippled rectangle (inside)"); + waitkey(); + + gfxop_draw_rectangle(state, gfx_rect(30, 30, 260, 100), green, GFX_LINE_MODE_FINE, GFX_LINE_STYLE_NORMAL); + gfxop_draw_rectangle(state, gfx_rect(40, 40, 240, 80), green, GFX_LINE_MODE_FINE, GFX_LINE_STYLE_STIPPLED); + update(); + MESSAGE("B.4: Fine rectangles (optional):\nTwo green rectangles should now have\noverwritten the _inner_ bounds\nof the red ones"); + waitkey(); +} + + +void +test_c(void) +{ + int i; + clear(); + update(); + MESSAGE("-- Test C: Boxes and updates --"); + waitkey(); + + gfxop_fill_box(state, gfx_rect(50, 50, 210, 80), red); + update(); + MESSAGE("C.0: Simple box"); + waitkey(); + + gfxop_disable_dirty_frames(state); + clear(); + gfxop_fill_box(state, gfx_rect(30, 30, 260, 100), white); + gfxop_fill_box(state, gfx_rect(50, 50, 210, 80), blue); + gfxop_enable_dirty_frames(state); + + MESSAGE("C.1: Partial propagation\nA white box containing a blue box\nhas been written to the back buffer.\nPress a key to propagate it."); + + for (i = 0; i <= 40; i++) { + gfxop_update_box(state, gfx_rect(i*4, 0 , 4, 150)); + gfxop_update_box(state, gfx_rect(317-i*4, 0 , 4, 150)); + gfxop_usleep(state, 4000); + } + + gfxop_disable_dirty_frames(state); + clear(); + gfxop_fill_box(state, gfx_rect(30, 30, 260, 100), red); + gfxop_enable_dirty_frames(state); + + MESSAGE("C.2: Single line propagation\nPress a key to propagate a red box.\nNote that dirty frame\naccounting must be dis-\n" + "abled for manual updates\nto work like this."); + waitkey(); + for (i = 159; i >= 0; i--) { + gfxop_update_box(state, gfx_rect(i, 0 , 1, 150)); + gfxop_update_box(state, gfx_rect(319-i, 0 , 1, 150)); + gfxop_usleep(state, 1000); + } + + clear(); + gfxop_draw_box(state, gfx_rect(150, 70, 20, 20), red, green, GFX_BOX_SHADE_FLAT); + gfxop_draw_box(state, gfx_rect(120, 70, 20, 20), red, green, GFX_BOX_SHADE_LEFT); + gfxop_draw_box(state, gfx_rect(180, 70, 20, 20), red, green, GFX_BOX_SHADE_RIGHT); + gfxop_draw_box(state, gfx_rect(150, 40, 20, 20), red, green, GFX_BOX_SHADE_UP); + gfxop_draw_box(state, gfx_rect(150, 100, 20, 20), red, green, GFX_BOX_SHADE_DOWN); + update(); + MESSAGE("C.3: Gradient boxes\nIf your driver supports gradient\nboxes, you should now see\nthe four outer boxes being\nred on the inside,--more--"); + waitkey(); + MESSAGE("C.3: Gradient boxes\n(cont.)and green on the\n outside.\n\nIf unsupported, all should be red."); + waitkey(); +} + + +void +test_d(void) +{ + rect_t line; + int pressed = 0; + sci_event_t event; + + event.type = 0; + + MESSAGE("-- Test D: Pointers and input --"); + clear(); + gfxop_fill_box(state, gfx_rect(30, 30, 260, 100), white); + gfxop_fill_box(state, gfx_rect(50, 50, 210, 80), blue); + waitkey(); + update(); + + gfxop_set_pointer_cursor(state, 1); + MESSAGE("D.0: Simple mouse pointer\nThis pointer's hot spot is at the\ntop left"); + waitkey(); + + gfxop_set_pointer_cursor(state, 0); + MESSAGE("D.1: Crosshair pointer\nHot spot is in the center.\nThis is a 'multicolor' pointer."); + waitkey(); + + MESSAGE("D.2: Mouse clicks\nPress and release buttons to\ndraw lines\nPress any key to continue"); + + while (event.type != SCI_EVT_KEYBOARD) { + event = gfxop_get_event(state, SCI_EVT_ANY); + + if (event.type == SCI_EVT_MOUSE_PRESS) { + + pressed = 1; + line.x = state->pointer_pos.x; + line.y = state->pointer_pos.y; + + } else if (event.type == SCI_EVT_MOUSE_RELEASE) + + if (pressed) { + point_t line_pt = gfx_point(line.x, line.y); + pressed = 0; + line.xl = state->pointer_pos.x - line.x; + line.yl = state->pointer_pos.y - line.y; + gfxop_draw_line(state, state->pointer_pos, line_pt, red, GFX_LINE_MODE_FAST, GFX_LINE_STYLE_NORMAL); + + if (line.xl < 0) { + line.x += line.xl; + line.xl = - line.xl; + } + + if (line.yl < 0) { + line.y += line.yl; + line.yl = - line.yl; + } + + gfxop_update_box(state, line); + } + + gfxop_usleep(state, 1000); + } + + event.type = 0; + + MESSAGE("D.3 Event test\nPress the space bar when finished"); + waitkey(); + + while (event.type != SCI_EVT_KEYBOARD || event.data != ' ') { + event = gfxop_get_event(state, SCI_EVT_ANY); + + if (event.type) + identify_event(event); + } +} + + +void +test_e(void) +{ + int x; + + gfxop_set_pointer_cursor(state, 1); + + MESSAGE("-- Test E: Pics and Views --"); + waitkey(); + + gfxop_new_pic(state, 0, 0, 0); + + explicit_clear_buffer(); + update(); + MESSAGE("E.0: Static buffer\nYou should now see a gray and\nblue background image now.\nIt is stored in the static\nbuffer, and --more--"); + waitkey(); + MESSAGE("E.0: Static buffer\n(cont.)then propagated to\nthe back buffer and then to\nthe front buffer."); + waitkey(); + + MESSAGE("E.1: Views and animation\nPress a key to see a cel move\nacross the screen\n(doing _full_ updates)"); + waitkey(); + + for (x = -20; x < 340; x++) { + clear_buffer(); + gfxop_draw_cel(state, 0, 0, 0, gfx_point(x, 40), white, 0); + update(); + gfxop_usleep(state, 10000); + } + + MESSAGE("E.2: Pic views\nFour pic views will now be added to\nthe static buffer"); + waitkey(); + + gfxop_draw_cel_static(state, 0, 0, 1, gfx_point(50, 100), white24, 0); + gfxop_draw_cel_static(state, 0, 0, 1, gfx_point(50, 20), white24, 0); + gfxop_draw_cel_static(state, 0, 0, 1, gfx_point(220, 20), white16, 0); + gfxop_draw_cel_static(state, 0, 0, 1, gfx_point(220, 100), white16, 0); + + update(); + MESSAGE("E.2: Pic views\nThe pic views should NOT\nbe visible yet!\n"); + waitkey(); + + explicit_clear_buffer(); + update(); + MESSAGE("E.2: Pic views\nNow they should be.\n"); + waitkey(); + + MESSAGE("E.3: Priority buffer\nPress a key to see two cels move\nacross the screen and re-\nspecting the Z buffer"); + waitkey(); + + for (x = -20; x < 340; x++) { + clear_buffer(); + gfxop_draw_cel(state, 0, 0, 2, gfx_point(x, 20), white8, 0); + gfxop_draw_cel(state, 0, 0, 2, gfx_point(x, 100), white16, 0); + update(); + gfxop_usleep(state, 10000); + } + + gfxop_add_to_pic(state, 1, 0, 0); + explicit_clear_buffer(); + update(); + MESSAGE("E.4: Adding to a pic\nSome new stuff should\nhave been added to the\nbackground pic now"); + waitkey(); + MESSAGE("E.5: Animation with partial updates\nIf you're running high-res,\nthis should be considerably\nfaster."); + waitkey(); + for (x = -20; x < 340; x++) { + gfxop_clear_box(state, gfx_rect(x-9, 40-8, 17, 16)); + gfxop_clear_box(state, gfx_rect(x-9, 70-8, 17, 16)); + gfxop_draw_cel(state, 0, 0, 2, gfx_point(x, 40), white16, 0); + gfxop_draw_cel(state, 0, 0, 2, gfx_point(x, 70), white16, 0); + gfxop_update(state); + /* gfxop_update_box(state, gfx_rect(x-1, 40, 17, 16)); */ + /* gfxop_update_box(state, gfx_rect(x-1, 70, 17, 16)); */ + gfxop_usleep(state, 10000); + } + waitkey(); +} + +void +test_wrap(int width, char *text) +{ + rect_t rect = gfx_rect(0, 0, width, 120); + gfx_text_handle_t *handle = gfxop_new_text(state, 0, + text, + width, ALIGN_LEFT, ALIGN_TOP, white, white, transparent, 0); + + gfxop_fill_box(state, rect, dblue); + gfxop_draw_rectangle(state, rect, blue, GFX_LINE_MODE_FAST, GFX_LINE_STYLE_NORMAL); + gfxop_draw_text(state, handle, gfx_rect(0, 0, 320, 150)); + gfxop_free_text(state, handle); +} + +void +test_f(void) +{ + int i; + int x, y; + gfx_text_handle_t *handle; + MESSAGE("-- Test F: Full font test --"); + waitkey(); + + handle = gfxop_new_text(state, 0, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890", + 320, ALIGN_LEFT, ALIGN_TOP, white, white, transparent, 0); + gfxop_draw_text(state, handle, gfx_rect(0, 0, 320, 150)); + gfxop_free_text(state, handle); + update(); + + MESSAGE("F.0: Font wrap\nYou should now see the alphabet\n(capitals, then small letters),\nand the numbers from 1 to 0" + "\nwrapped with a maxwidth of\n320 (screen width)"); + waitkey(); + + MESSAGE("F.1: Font wrap:\nMisc. wrap widths\nAll text should be /within/ the\nblue box (it may touch\nthe border, though)"); + waitkey(); + for (i = 200; i > 50; i -= 7) { + clear_buffer(); + test_wrap(i, "\"Far out in the uncharted regions of the western spiral arm of the galaxy...\""); + update(); + MESSAGE1("\nwidth=%d", i); + waitkey(); + } + + handle = gfxop_new_text(state, 0, "And now for something completely different.", + 320, ALIGN_LEFT, ALIGN_TOP, red, green, black, 0); + gfxop_draw_text(state, handle, gfx_rect(0, 0, 320, 150)); + gfxop_free_text(state, handle); + update(); + MESSAGE("F.2: Dithered text\nThis text should now be dithered\nred/green on black background"); + waitkey(); + + clear_buffer(); + handle = gfxop_new_text(state, 0, "foo!", + 320, ALIGN_CENTER, ALIGN_CENTER, blue, blue, transparent, 0); + x = 10; y = 10; + + for (i = 0; i < 1000; i++) { + x = (x+(70 + (i / y) + y*y*x / (i+1))); + y = (y+(30 + (i / x) + x*x*y / (i+1))); + gfxop_draw_text(state, handle, gfx_rect(x % 320, y % 140, 0, 0)); + } + + gfxop_free_text(state, handle); + update(); + MESSAGE("F.3: Multiple draws\nText handles may be used more\nthan once\n(1000 times here)"); + waitkey(); +} + +void +do_tests(char *conf) +{ + init_colors(); + + + if (strchr(conf, 'a')) + if (test_a()) + return; + + if (strchr(conf, 'b')) + test_b(); + + if (strchr(conf, 'c')) + test_c(); + + if (strchr(conf, 'd')) + test_d(); + + if (strchr(conf, 'e')) + test_e(); + + if (strchr(conf, 'f')) + test_f(); +} + +int +c_quit(void *S) +{ + exit(0); + return 0; /* hahaha */ +} + +int +main(int argc, char **argv) +{ + gfx_driver_t *drv = NULL; + char c; + + strcpy(tests, ALL_TESTS); + + printf("gfx_test Copyright (C) 2000 Christoph Reichenbach\nThis program is provided WITHOUT WARRANTY of any kind. Please\n" + "refer to the file COPYING that should have come with this\ndistribution for licensing details.\n\n"); + + while ((c = getopt(argc, argv, "nhslc:g:x:y:t:")) > -1) + switch (c) { + + case 'h': + printf("Usage: gfx_test [-l] [-h] [-g driver] [-x xfact] [-y yfact] [-c bpp]\n" + "-l: List all graphics targets\n" + "-x: Set x resolution scale factor\n" + "-y: Set y resolution scale factor\n" + "-s: Skip intro text and getchar()\n" + "-c: Set bytes per pixel\n" + "-h: Display this help message\n" + "-n: Immediately stop after displaying (for performance tests)\n" + "-g: Select any of the graphics drivers shown with -l\n" + "-t: Select the tests to run:\n" + "\ta: Text display and basic input\n" + "\tb: Lines\n" + "\tc: Boxes and updates\n" + "\td: Pointers and input\n" + "\te: Pics and Views\n" + " -- for example, use \"-t abc\" to run tests A, B, and C\n" + ); + return 0; + + case 'n': nowait = 1; + break; + + case 's': skip_intro = 1; + break; + + case 'g': if (driver) sci_free(driver); + driver = sci_strdup(optarg); + break; + + case 'l': { + int first = 1; + int i = 0; + + printf("Available graphics drivers: "); + while (gfx_get_driver_name(i)) { + if (!first) + printf(", "); + first = 0; + printf("%s", gfx_get_driver_name(i++)); + } + printf("\n"); + } break; + + case 't': + strcpy(tests, optarg); + break; + + case 'x': + set_mode = xres = atoi(optarg); + if (xres < 1) { + fprintf(stderr,"Invalid x scale factor!\n"); + return 1; + } + break; + + case 'y': + set_mode = yres = atoi(optarg); + if (yres < 1) { + fprintf(stderr,"Invalid y scale factor!\n"); + return 1; + } + break; + + case 'c': + set_mode = color_mode = atoi(optarg); + if (color_mode < 1 || color_mode > 4) { + fprintf(stderr,"Invalid number of bytes per pixel!\n"); + return 1; + } + break; + + default: + fprintf(stderr,"Run 'gfx_test -h' for help\n"); + return 1; + } + + drv = gfx_find_driver("/tmp", driver); + + if (drv) { + printf("Using graphics driver '%s'\n", drv->name); + + if (!skip_intro) { + printf("Testing will now start. The first test will check whether displaying\n" + "text and reading input works; it will display a message and wait for twenty\n" + "seconds for you to press the space bar. If it does not register a space bar\n" + "keypress, it will abort.\n" + "Note that it can happen that no text is displayed, but you still are able\n" + "to proceed to the next test by pressing the space bar. However, unless you\n" + "are running in windowed mode, you will not be able to read which kinds of\n" + "tests are being run, so it is recommended that you fix pixmap displaying\n" + "and back-to-front-buffer updating first, so that the text can be displayed\n" + "correctly.\n" + "-- Press any key to start the first test or Ctrl-C to abort --\n"); + getchar(); + } + + if (init_driver(drv)) { + fprintf(stderr,"Initialization failed!\n"); + return 1; + } + + do_tests(tests); + + if (gfxop_exit(state)) { + fprintf(stderr,"Something weird happened while exitting...\n"); + } + } else { + fprintf(stderr,"No graphics driver found!\n"); + return 1; + } + + return 0; +} + + + +byte builtin_font[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, + 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e, + 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, + 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, + 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0xd6, 0x10, 0x38, + 0x10, 0x38, 0x7c, 0xfe, 0xfe, 0x7c, 0x10, 0x38, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, + 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, + 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, + 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, + 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78, + 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, + 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0, + 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0, + 0x18, 0xdb, 0x3c, 0xe7, 0xe7, 0x3c, 0xdb, 0x18, + 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00, + 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00, + 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, + 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00, + 0x3e, 0x61, 0x3c, 0x66, 0x66, 0x3c, 0x86, 0x7c, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00, + 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff, + 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, + 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, + 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, 0x00, + 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, + 0x18, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x18, 0x00, + 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00, + 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00, + 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, + 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, + 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, + 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, + 0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, + 0x7c, 0xc6, 0x06, 0x1c, 0x30, 0x66, 0xfe, 0x00, + 0x7c, 0xc6, 0x06, 0x3c, 0x06, 0xc6, 0x7c, 0x00, + 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00, + 0xfe, 0xc0, 0xc0, 0xfc, 0x06, 0xc6, 0x7c, 0x00, + 0x38, 0x60, 0xc0, 0xfc, 0xc6, 0xc6, 0x7c, 0x00, + 0xfe, 0xc6, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00, + 0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0x7c, 0x00, + 0x7c, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, + 0x06, 0x0c, 0x18, 0x30, 0x18, 0x0c, 0x06, 0x00, + 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, + 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, + 0x7c, 0xc6, 0x0c, 0x18, 0x18, 0x00, 0x18, 0x00, + 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00, + 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00, + 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00, + 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, + 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00, + 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00, + 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3a, 0x00, + 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, + 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, + 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, + 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, + 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00, + 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, + 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xce, 0x7c, 0x0e, + 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00, + 0x3c, 0x66, 0x30, 0x18, 0x0c, 0x66, 0x3c, 0x00, + 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, + 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, + 0xc6, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0xc6, 0x00, + 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x00, + 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00, + 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, + 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, + 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0xe0, 0x60, 0x7c, 0x66, 0x66, 0x66, 0xdc, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc6, 0x7c, 0x00, + 0x1c, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, + 0x3c, 0x66, 0x60, 0xf8, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00, + 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x06, 0x00, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, + 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00, + 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6, 0x00, + 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e, + 0x00, 0x00, 0xdc, 0x76, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x7e, 0xc0, 0x7c, 0x06, 0xfc, 0x00, + 0x30, 0x30, 0xfc, 0x30, 0x30, 0x36, 0x1c, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, + 0x00, 0x00, 0xc6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, + 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0xfc, + 0x00, 0x00, 0x7e, 0x4c, 0x18, 0x32, 0x7e, 0x00, + 0x0e, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0e, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x70, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x70, 0x00, + 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x18, 0x00, + 0x18, 0x18, 0x7e, 0xc0, 0xc0, 0x7e, 0x18, 0x18, + 0x38, 0x6c, 0x64, 0xf0, 0x60, 0x66, 0xfc, 0x00, + 0x00, 0xc6, 0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0x00, + 0x66, 0x66, 0x3c, 0x7e, 0x18, 0x7e, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, + 0x3e, 0x61, 0x3c, 0x66, 0x66, 0x3c, 0x86, 0x7c, + 0x00, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x81, 0x9d, 0xa1, 0xa1, 0x9d, 0x81, 0x7e, + 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, + 0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x81, 0xb9, 0xa5, 0xb9, 0xa5, 0x81, 0x7e, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x7e, 0x00, + 0x78, 0x0c, 0x18, 0x30, 0x7c, 0x00, 0x00, 0x00, + 0x78, 0x0c, 0x38, 0x0c, 0x78, 0x00, 0x00, 0x00, + 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0xc0, + 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0x38, + 0x18, 0x38, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, + 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00, + 0x63, 0xe6, 0x6c, 0x7a, 0x36, 0x6a, 0xdf, 0x06, + 0x63, 0xe6, 0x6c, 0x7e, 0x33, 0x66, 0xcc, 0x0f, + 0xe1, 0x32, 0xe4, 0x3a, 0xf6, 0x2a, 0x5f, 0x86, + 0x18, 0x00, 0x18, 0x18, 0x30, 0x63, 0x3e, 0x00, + 0x18, 0x0c, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x00, + 0x30, 0x60, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x00, + 0x7c, 0x82, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x00, + 0x76, 0xdc, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x00, + 0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, + 0x38, 0x6c, 0x7c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, + 0x3e, 0x6c, 0xcc, 0xfe, 0xcc, 0xcc, 0xce, 0x00, + 0x7c, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x0c, 0x78, + 0x30, 0x18, 0xfe, 0xc0, 0xfc, 0xc0, 0xfe, 0x00, + 0x18, 0x30, 0xfe, 0xc0, 0xf8, 0xc0, 0xfe, 0x00, + 0x7c, 0x82, 0xfe, 0xc0, 0xfc, 0xc0, 0xfe, 0x00, + 0xc6, 0x00, 0xfe, 0xc0, 0xfc, 0xc0, 0xfe, 0x00, + 0x30, 0x18, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x0c, 0x18, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x3c, 0x42, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x66, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0xf8, 0x6c, 0x66, 0xf6, 0x66, 0x6c, 0xf8, 0x00, + 0x76, 0xdc, 0x00, 0xe6, 0xf6, 0xde, 0xce, 0x00, + 0x0c, 0x06, 0x38, 0x6c, 0xc6, 0x6c, 0x38, 0x00, + 0x30, 0x60, 0x38, 0x6c, 0xc6, 0x6c, 0x38, 0x00, + 0x7c, 0x82, 0x38, 0x6c, 0xc6, 0x6c, 0x38, 0x00, + 0x76, 0xdc, 0x38, 0x6c, 0xc6, 0x6c, 0x38, 0x00, + 0xc6, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x00, + 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, 0x00, + 0x3a, 0x6c, 0xce, 0xd6, 0xe6, 0x6c, 0xb8, 0x00, + 0x60, 0x30, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x18, 0x30, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x7c, 0x82, 0x00, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x0c, 0x18, 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x00, + 0xf0, 0x60, 0x7c, 0x66, 0x7c, 0x60, 0xf0, 0x00, + 0x78, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xcc, 0x00, + 0x30, 0x18, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0x18, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0x7c, 0x82, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0x76, 0xdc, 0x7c, 0x06, 0x7e, 0xc6, 0x7e, 0x00, + 0xc6, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0x7e, 0x12, 0xfe, 0x90, 0xfe, 0x00, + 0x00, 0x00, 0x7e, 0xc0, 0xc0, 0x7e, 0x0c, 0x38, + 0x30, 0x18, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, + 0x0c, 0x18, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, + 0x7c, 0x82, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, + 0xc6, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, + 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00, + 0x0c, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00, + 0x7c, 0x82, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x30, 0x7e, 0x0c, 0x7c, 0xcc, 0xcc, 0x78, 0x00, + 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x00, + 0x30, 0x18, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x0c, 0x18, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x7c, 0x82, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x76, 0xdc, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x00, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x02, 0x7c, 0xce, 0xd6, 0xe6, 0x7c, 0x80, + 0x60, 0x30, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x18, 0x30, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x78, 0x84, 0x00, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x18, 0x30, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0xfc, + 0xe0, 0x60, 0x7c, 0x66, 0x66, 0x7c, 0x60, 0xf0, + 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0xfc +}; + + + + diff --git a/engines/sci/gfx/gfx_tools.c b/engines/sci/gfx/gfx_tools.c new file mode 100644 index 0000000000..b82627c896 --- /dev/null +++ b/engines/sci/gfx/gfx_tools.c @@ -0,0 +1,465 @@ +/*************************************************************************** + gfx_tools.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include + +/* set optimisations for Win32: */ +#ifdef _WIN32 +# include +# ifndef SATISFY_PURIFY +# pragma intrinsic( memcpy, memset ) +# endif +#endif + +rect_t gfx_rect_fullscreen = {0, 0, 320, 200}; + +void +gfx_clip_box_basic(rect_t *box, int maxx, int maxy) +{ + if (box->x < 0) + box->x = 0; + + if (box->y < 0) + box->y = 0; + + if (box->x + box->xl > maxx) + box->xl = maxx - box->x + 1; + + if (box->y + box->yl > maxy) + box->yl = maxy - box->y + 1; +} + + +gfx_mode_t * +gfx_new_mode(int xfact, int yfact, int bytespp, unsigned int red_mask, unsigned int green_mask, + unsigned int blue_mask, unsigned int alpha_mask, int red_shift, int green_shift, + int blue_shift, int alpha_shift, int palette, int flags) +{ + gfx_mode_t *mode = (gfx_mode_t*)sci_malloc(sizeof(gfx_mode_t)); +#ifdef SATISFY_PURIFY + memset(mode, 0, sizeof(gfx_mode_t)); +#endif + + mode->xfact = xfact; + mode->yfact = yfact; + mode->bytespp = bytespp; + mode->red_mask = red_mask; + mode->green_mask = green_mask; + mode->blue_mask = blue_mask; + mode->alpha_mask = alpha_mask; + mode->red_shift = red_shift; + mode->green_shift = green_shift; + mode->blue_shift = blue_shift; + mode->alpha_shift = alpha_shift; + mode->flags = flags; + + if (palette) { + mode->palette = (gfx_palette_t*)sci_malloc(sizeof(gfx_palette_t)); +#ifdef SATISFY_PURIFY + memset(mode->palette, 0, sizeof(gfx_palette_t)); +#endif + mode->palette->max_colors_nr = palette; + mode->palette->colors = (gfx_palette_color_t*)sci_calloc(sizeof(gfx_palette_color_t), palette); /* Initialize with empty entries */ + } else mode->palette = NULL; + + return mode; +} + + +void +gfx_free_mode(gfx_mode_t *mode) +{ + if (mode->palette) { + free(mode->palette->colors); + free(mode->palette); + } + free(mode); +} + + + +void +gfx_copy_pixmap_box_i(gfx_pixmap_t *dest, gfx_pixmap_t *src, rect_t box) +{ + int width, height; + int offset; + + if ((dest->index_xl != src->index_xl) || (dest->index_yl != src->index_yl)) + return; + + gfx_clip_box_basic(&box, dest->index_xl, dest->index_yl); + + if (box.xl <= 0 || box.yl <= 0) + return; + + height = box.yl; + width = box.xl; + + offset = box.x + (box.y * dest->index_xl); + + while (height--) { + memcpy(dest->index_data + offset, src->index_data + offset, width); + offset += dest->index_xl; + } +} + + +gfx_pixmap_t * +gfx_clone_pixmap(gfx_pixmap_t *pxm, gfx_mode_t *mode) +{ + gfx_pixmap_t *clone = (gfx_pixmap_t*)sci_malloc(sizeof(gfx_pixmap_t)); + *clone = *pxm; + clone->index_data = NULL; + clone->colors = NULL; + clone->data = NULL; + gfx_pixmap_alloc_data(clone, mode); + + memcpy(clone->data, pxm->data, clone->data_size); + if (clone->alpha_map) { + clone->alpha_map = (byte *) sci_malloc(clone->xl * clone->yl); + memcpy(clone->alpha_map, pxm->alpha_map, clone->xl * clone->yl); + } + + return clone; +} + +gfx_pixmap_t * +gfx_new_pixmap(int xl, int yl, int resid, int loop, int cel) +{ + gfx_pixmap_t *pxm = (gfx_pixmap_t*)sci_malloc(sizeof(gfx_pixmap_t)); +#ifdef SATISFY_PURIFY + memset(pxm, 0, sizeof(gfx_pixmap_t)); +#endif + + pxm->alpha_map = NULL; + pxm->data = NULL; + pxm->internal.info = NULL; + pxm->colors = NULL; + pxm->internal.handle = 0; + + pxm->index_xl = xl; + pxm->index_yl = yl; + + pxm->ID = resid; + pxm->loop = loop; + pxm->cel = cel; + + pxm->index_data = NULL; + + pxm->flags = 0; + + pxm->color_key = 0xff; + + return pxm; +} + + +void +gfx_free_pixmap(gfx_driver_t *driver, gfx_pixmap_t *pxm) +{ + if (driver) { + if (pxm->flags & GFX_PIXMAP_FLAG_INSTALLED) { + if (driver->capabilities & GFX_CAPABILITY_PIXMAP_REGISTRY) + driver->unregister_pixmap(driver, pxm); + } + + if (driver->mode->palette + && pxm->flags & GFX_PIXMAP_FLAG_PALETTE_ALLOCATED + && !(pxm->flags & GFX_PIXMAP_FLAG_DONT_UNALLOCATE_PALETTE) + && !(pxm->flags & GFX_PIXMAP_FLAG_EXTERNAL_PALETTE)) { + int i; + int error = 0; + GFXDEBUG("UNALLOCATING %d\n", pxm->colors_nr); + for (i = 0; i < pxm->colors_nr; i++) + if (gfx_free_color(driver->mode->palette, pxm->colors + i)) + error++; + + if (error) { + GFXWARN("%d errors occured while freeing %d colors of pixmap with ID %06x/%d/%d\n", + error, pxm->colors_nr,pxm->ID, pxm->loop, pxm->cel); + } + } + } + + if (pxm->index_data) + free(pxm->index_data); + + if (pxm->alpha_map) + free(pxm->alpha_map); + + if (pxm->data) + free(pxm->data); + + if (pxm->colors && !(pxm->flags & GFX_PIXMAP_FLAG_EXTERNAL_PALETTE)) + free(pxm->colors); + + free(pxm); +} + + +gfx_pixmap_t * +gfx_pixmap_alloc_index_data(gfx_pixmap_t *pixmap) +{ + int size; + + if (pixmap->index_data) { + GFXWARN("Attempt to allocate pixmap index data twice!\n"); + return pixmap; + } + + size = pixmap->index_xl * pixmap->index_yl; + if (!size) + size = 1; + + pixmap->index_data = (byte*)sci_malloc(size); + + memset(pixmap->index_data, 0, size); + + return pixmap; +} + + +gfx_pixmap_t * +gfx_pixmap_free_index_data(gfx_pixmap_t *pixmap) +{ + if (!pixmap->index_data) { + GFXWARN("Attempt to free pixmap index data twice!\n"); + return pixmap; + } + + free(pixmap->index_data); + pixmap->index_data = NULL; + return pixmap; +} + + +gfx_pixmap_t * +gfx_pixmap_alloc_data(gfx_pixmap_t *pixmap, gfx_mode_t *mode) +{ + int size; + + if (pixmap->data) { + GFXWARN("Attempt to allocate pixmap data twice!\n"); + return pixmap; + } + + if (pixmap->flags & GFX_PIXMAP_FLAG_SCALED_INDEX) { + pixmap->xl = pixmap->index_xl; + pixmap->yl = pixmap->index_yl; + } else { + pixmap->xl = pixmap->index_xl * mode->xfact; + pixmap->yl = pixmap->index_yl * mode->yfact; + } + + size = pixmap->xl * pixmap->yl * mode->bytespp; + if (!size) + size = 1; + + pixmap->data = (byte*)sci_malloc(pixmap->data_size = size); + return pixmap; +} + + +gfx_pixmap_t * +gfx_pixmap_free_data(gfx_pixmap_t *pixmap) +{ + if (!pixmap->data) { + GFXWARN("Attempt to free pixmap data twice!\n"); + return pixmap; + } + + free(pixmap->data); + pixmap->data = NULL; + return pixmap; +} + + +int +gfx_alloc_color(gfx_palette_t *pal, gfx_pixmap_color_t *color) +{ + int i; + int dr, dg, db; /* deltas */ + int bestdelta = 1 + ((0x100 * 0x100) * 3); + int bestcolor = -1; + int firstfree = -1; + + if (pal == NULL) + return GFX_OK; + + if (pal->max_colors_nr <= 0) { + GFXERROR("Palette has zero or less color entries!\n"); + return GFX_ERROR; + } + + + if (color->global_index != GFX_COLOR_INDEX_UNMAPPED) { +#if 0 + GFXDEBUG("Attempt to allocate color twice: index 0x%d (%02x/%02x/%02x)!\n", + color->global_index, color->r, color->g, color->b); +#endif + return GFX_OK; + } + + for (i = 0; i < pal->max_colors_nr; i++) { + gfx_palette_color_t *pal_color = pal->colors + i; + + if (pal_color->lockers) { + int delta; + + dr = abs(pal_color->r - color->r); + dg = abs(pal_color->g - color->g); + db = abs(pal_color->b - color->b); + + if (dr == 0 && dg == 0 && db == 0) { + color->global_index = i; + return GFX_OK; + } + + delta = (dr * dr) + (dg * dg) + (db * db); + if (delta < bestdelta) { + bestdelta = delta; + bestcolor = i; + } + } else + if (firstfree == -1) + firstfree = i; + } + + if (firstfree != -1) { + pal->colors[firstfree].r = color->r; + pal->colors[firstfree].g = color->g; + pal->colors[firstfree].b = color->b; + pal->colors[firstfree].lockers = 1; + color->global_index = firstfree; + + return 42; /* positive value to indicate that this color still needs to be set */ + } + + color->global_index = bestcolor; + +// GFXWARN("Out of palette colors- doing approximated mapping!\n"); + return GFX_OK; +} + + +int +gfx_free_color(gfx_palette_t *pal, gfx_pixmap_color_t *color) +{ + gfx_palette_color_t *palette_color = pal->colors + color->global_index; + + if (!pal) + return GFX_OK; + + if (color->global_index == GFX_COLOR_INDEX_UNMAPPED) { + GFXWARN("Attempt to free unmapped color %02x/%02x/%02x!\n", color->r, color->g, color->b); + BREAKPOINT(); + return GFX_ERROR; + } + + if (color->global_index >= pal->max_colors_nr) { + GFXERROR("Attempt to free invalid color index %d (%02x/%02x/%02x)!\n", + color->global_index, color->r, color->g, color->b); + return GFX_ERROR; + } + + if (!palette_color->lockers) { + GFXERROR("Attempt to free unused color index %d (%02x/%02x/%02x)!\n", + color->global_index, color->r, color->g, color->b); + return GFX_ERROR; + } + + if (palette_color->lockers != GFX_COLOR_SYSTEM) + --(palette_color->lockers); + + color->global_index = GFX_COLOR_INDEX_UNMAPPED; + return GFX_OK; +} + + +gfx_pixmap_t * +gfx_pixmap_scale_index_data(gfx_pixmap_t *pixmap, gfx_mode_t *mode) +{ + byte *old_data, *new_data, *initial_new_data; + byte *linestart; + int linewidth; + int xl, yl; + int i, yc; + int xfact = mode->xfact; + int yfact = mode->yfact; + + if (xfact == 1 && yfact == 1) + return pixmap; + + if (!pixmap) + return NULL; + + if (pixmap->flags & GFX_PIXMAP_FLAG_SCALED_INDEX) + return pixmap; /* Already done */ + + old_data = pixmap->index_data; + + if (!old_data) { + GFXERROR("Attempt to scale index data without index data!\n"); + return pixmap; + } + + xl = pixmap->index_xl; + yl = pixmap->index_yl; + linewidth = xfact * xl; + initial_new_data = new_data = (byte *) sci_malloc(linewidth * yfact * yl); + + for (yc = 0; yc < yl; yc++) { + + linestart = new_data; + + if (xfact == 1) { + memcpy(new_data, old_data, linewidth); + new_data += linewidth; + old_data += linewidth; + } else for (i = 0; i < xl; i++) { + byte fillc = *old_data++; + memset(new_data, fillc, xfact); + new_data += xfact; + } + + for (i = 1; i < yfact; i++) { + memcpy(new_data, linestart, linewidth); + new_data += linewidth; + } + } + + free(pixmap->index_data); + pixmap->index_data = initial_new_data; + + pixmap->flags |= GFX_PIXMAP_FLAG_SCALED_INDEX; + + pixmap->index_xl = linewidth; + pixmap->index_yl *= yfact; + + return pixmap; +} diff --git a/engines/sci/gfx/menubar.c b/engines/sci/gfx/menubar.c new file mode 100644 index 0000000000..ff4cb4a11e --- /dev/null +++ b/engines/sci/gfx/menubar.c @@ -0,0 +1,536 @@ +/*************************************************************************** + menubar.c Copyright (C) 1999,2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ +/* Management and drawing operations for the SCI0 menu bar */ +/* I currently assume that the hotkey information used in the menu bar is NOT +** used for any actual actions on behalf of the interpreter. +*/ + +#include +#include +#include + +#define SIZE_INF 32767 + +/* +static int __active = 0; + +inline void* +__my_malloc(long size, char *function, int line) +{ + void *retval = sci_malloc(size); + __active++; + fprintf(stderr,"[%d] line %d, %s: malloc(%d) -> %p\n", __active, line, function, size, retval); + return retval; +} + +inline void* +__my_realloc(void *origin, long size, char *function, int line) +{ + void *retval = sci_realloc(origin, size); + fprintf(stderr,"line %d, %s: realloc(%p, %d) -> %p\n", line, function, origin, size, retval); + return retval; +} + +inline void +__my_free(void *origin, char *function, int line) +{ + free(origin); + fprintf(stderr,"[%d] line %d, %s: free(%p)\n", __active, line, function, origin); + __active--; +} + +#define malloc(x) __my_malloc(x, __PRETTY_FUNCTION__, __LINE__) +#define realloc(x,y) __my_realloc(x,y, __PRETTY_FUNCTION__, __LINE__) +#define free(x) __my_free(x, __PRETTY_FUNCTION__, __LINE__) +*/ + +menubar_t * +menubar_new() +{ + menubar_t *tmp = (menubar_t*)sci_malloc(sizeof(menubar_t)); + tmp->menus_nr = 0; + + return tmp; +} + +void +menubar_free(menubar_t *menubar) +{ + int i; + + for (i = 0; i < menubar->menus_nr; i++) { + menu_t *menu = &(menubar->menus[i]); + int j; + + for (j = 0; j < menu->items_nr; j++) { + if (menu->items[j].keytext) + free (menu->items[j].keytext); + if (menu->items[j].text) + free (menu->items[j].text); + } + + free(menu->items); + free(menu->title); + } + + if (menubar->menus_nr) + free (menubar->menus); + + free(menubar); +} + + +int +_menubar_add_menu_item(gfx_state_t *state, menu_t *menu, int type, char *left, char *right, + int font, int key, int modifiers, int tag, reg_t text_pos) +/* Returns the total text size, plus MENU_BOX_CENTER_PADDING if (right != NULL) */ +{ + menu_item_t *item; + int total_left_size = 0; + int width, height; + + if (menu->items_nr == 0) { + menu->items = (menu_item_t *) sci_malloc(sizeof(menu_item_t)); + menu->items_nr = 1; + } else menu->items = (menu_item_t *) sci_realloc(menu->items, sizeof(menu_item_t) * ++(menu->items_nr)); + + item = &(menu->items[menu->items_nr - 1]); + + memset(item, 0, sizeof(menu_item_t)); + + if ((item->type = type) == MENU_TYPE_HBAR) + return 0; + + /* else assume MENU_TYPE_NORMAL */ + item->text = left; + if (right) { + int end = strlen(right); + item->keytext = right; + while (end && isspace(right[end])) + right[end--] = 0; /* Remove trailing whitespace */ + item->flags = MENU_ATTRIBUTE_FLAGS_KEY; + item->key = key; + item->modifiers = modifiers; + } else { + item->keytext=NULL; + item->flags = 0; + } + + if (right) { + gfxop_get_text_params(state, font, right, SIZE_INF, &width, &height, 0, + NULL, NULL, NULL); + total_left_size = MENU_BOX_CENTER_PADDING + (item->keytext_size = width); + } + + item->enabled = 1; + item->tag = tag; + item->text_pos = text_pos; + gfxop_get_text_params(state, font, left, SIZE_INF, &width, &height, 0, + NULL, NULL, NULL); + + return total_left_size + width; +} + +void +menubar_add_menu(gfx_state_t *state, menubar_t *menubar, char *title, char *entries, int font, + reg_t entries_base) +{ + int i, add_freesci = 0; + menu_t *menu; + char tracker; + char *left = NULL, *right; + reg_t left_origin; + int string_len = 0; + int tag = 0, c_width, max_width = 0; + int height; + + if (menubar->menus_nr == 0) { +#ifdef MENU_FREESCI_BLATANT_PLUG + add_freesci = 1; +#endif + menubar->menus = (menu_t*)sci_malloc(sizeof(menu_t)); + menubar->menus_nr = 1; + } else menubar->menus = (menu_t*)sci_realloc(menubar->menus, ++(menubar->menus_nr) * sizeof (menu_t)); + + menu = &(menubar->menus[menubar->menus_nr-1]); + memset(menu, 0, sizeof(menu_t)); + menu->items_nr = 0; + menu->title = sci_strdup(title); + + gfxop_get_text_params(state, font, menu->title, SIZE_INF, &(menu->title_width), &height, 0, + NULL, NULL, NULL); + + do { + tracker = *entries++; + entries_base.offset++; + + if (!left) { /* Left string not finished? */ + + if (tracker == '=') { /* Hit early-SCI tag assignment? */ + + left = sci_strndup(entries - string_len - 1, string_len); + tag = atoi(entries++); + tracker = *entries++; + } + if ((tracker == 0 && string_len > 0) || (tracker == '=') || (tracker == ':')) { /* End of entry */ + int entrytype = MENU_TYPE_NORMAL; + char *inleft; + reg_t beginning; + + if (!left) + left = sci_strndup(entries - string_len - 1, string_len); + + inleft = left; + while (isspace(*inleft)) + inleft++; /* Seek beginning of actual string */ + + if (!strncmp(inleft, MENU_HBAR_STRING_1, strlen(MENU_HBAR_STRING_1)) + || !strncmp(inleft, MENU_HBAR_STRING_2, strlen(MENU_HBAR_STRING_2)) + || !strncmp(inleft, MENU_HBAR_STRING_3, strlen(MENU_HBAR_STRING_3))) + { + entrytype = MENU_TYPE_HBAR; /* Horizontal bar */ + free(left); + left = NULL; + } + + beginning = entries_base; beginning.offset -= string_len + 1; + c_width = _menubar_add_menu_item(state, menu, entrytype, left, NULL, font, 0, 0, tag, + beginning); + if (c_width > max_width) + max_width = c_width; + + string_len = 0; + left = NULL; /* Start over */ + + } else if (tracker == '`') { /* Start of right string */ + + if (!left) + { + left_origin = entries_base; left_origin.offset -= string_len + 1; + left = sci_strndup(entries - string_len -1, string_len); + } + string_len = 0; /* Continue with the right string */ + + } + else string_len++; /* Nothing special */ + + } else { /* Left string finished => working on right string */ + if ((tracker == ':') || (tracker == 0)) { /* End of entry */ + int key, modifiers = 0; + + right = sci_strndup(entries - string_len - 1, string_len); + + if (right[0] == '#') { + right[0] = SCI_SPECIAL_CHAR_FUNCTION; /* Function key */ + + key = SCI_K_F1 + ((right[1] - '1') << 8); + + if (right[1] == '0') + key = SCI_K_F10; /* F10 */ + + if (right[2]=='=') { + tag = atoi(right + 3); + right[2] = 0; + }; + } + else if (right[0] == '@') { /* Alt key */ + right[0] = SCI_SPECIAL_CHAR_ALT; /* ALT */ + key = right[1]; + modifiers = SCI_EVM_ALT; + + if ((key >= 'a') && (key <= 'z')) + right[1] = key - 'a' + 'A'; + + if (right[2]=='=') { + tag = atoi(right+3); + right[2] = 0; + }; + + } + else { + + if (right[0] == '^') { + right[0] = SCI_SPECIAL_CHAR_CTRL; /* Control key - there must be a replacement... */ + key = right[1]; + modifiers = SCI_EVM_CTRL; + + if ((key >= 'a') && (key <= 'z')) + right[1] = key - 'a' + 'A'; + + if (right[2]=='=') { + tag = atoi(right+3); + right[2] = 0; + } + + } + else { + key = right[0]; + if ((key >= 'a') && (key <= 'z')) + right[0] = key - 'a' + 'A'; + + if (right[1]=='=') { + tag=atoi(right+2); + right[1] = 0; + } + } + + if ((key >= 'A') && (key <= 'Z')) + key = key - 'A' + 'a'; /* Lowercase the key */ + } + + + i = strlen(right); + + while (i>0 && right[--i] == ' ') + right[i] = 0; /* Cut off chars to the right */ + + c_width = _menubar_add_menu_item(state, menu, MENU_TYPE_NORMAL, left, right, font, key, + modifiers, tag, left_origin); + tag = 0; + if (c_width > max_width) + max_width = c_width; + + string_len = 0; + left = NULL; /* Start over */ + + } else string_len++; /* continuing entry */ + } /* right string finished */ + + } while (tracker); + +#ifdef MENU_FREESCI_BLATANT_PLUG + if (add_freesci) { + + char *freesci_text = sci_strdup ("About FreeSCI"); + c_width = _menubar_add_menu_item(state, menu, MENU_TYPE_NORMAL, freesci_text, NULL, font, 0, 0, 0, NULL_REG); + if (c_width > max_width) + max_width = c_width; + + menu->items[menu->items_nr-1].flags = MENU_FREESCI_BLATANT_PLUG; + } +#endif /* MENU_FREESCI_BLATANT_PLUG */ + menu->width = max_width; +} + +int +menubar_match_key(menu_item_t *item, int message, int modifiers) +{ + if ((item->key == message) + && ((modifiers & (SCI_EVM_CTRL | SCI_EVM_ALT)) == item->modifiers)) + return 1; + + if (message == '\t' + && item->key == 'i' + && ((modifiers & (SCI_EVM_CTRL | SCI_EVM_ALT)) == 0) + && item->modifiers == SCI_EVM_CTRL) + return 1; /* Match TAB to ^I */ + + return 0; +} + +int +menubar_set_attribute(state_t *s, int menu_nr, int item_nr, int attribute, reg_t value) +{ + menubar_t *menubar = s->menubar; + menu_item_t *item; + + if ((menu_nr < 0) || (item_nr < 0)) + return 1; + + if ((menu_nr >= menubar->menus_nr) || (item_nr >= menubar->menus[menu_nr].items_nr)) + return 1; + + item = menubar->menus[menu_nr].items + item_nr; + + switch (attribute) { + + case MENU_ATTRIBUTE_SAID: + if (value.segment) { + item->said_pos = value; + memcpy(item->said, kernel_dereference_bulk_pointer(s, value, 0), MENU_SAID_SPEC_SIZE); /* Copy Said spec */ + item->flags |= MENU_ATTRIBUTE_FLAGS_SAID; + + } else + item->flags &= ~MENU_ATTRIBUTE_FLAGS_SAID; + + break; + + case MENU_ATTRIBUTE_TEXT: + free(item->text); + assert(value.segment); + item->text = sci_strdup(kernel_dereference_char_pointer(s, value, 0)); + item->text_pos = value; + break; + + case MENU_ATTRIBUTE_KEY: + if (item->keytext) + free(item->keytext); + + if (value.segment) { + + /* FIXME: What happens here if is an extended key? Potential bug. LS */ + item->key = value.offset; + item->modifiers = 0; + item->keytext = (char*)sci_malloc(2); + item->keytext[0] = value.offset; + item->keytext[1] = 0; + item->flags |= MENU_ATTRIBUTE_FLAGS_KEY; + if ((item->key >= 'A') && (item->key <= 'Z')) + item->key = item->key - 'A' + 'a'; /* Lowercase the key */ + + + } else { + + item->keytext = NULL; + item->flags &= ~MENU_ATTRIBUTE_FLAGS_KEY; + + } + break; + + case MENU_ATTRIBUTE_ENABLED: + item->enabled = value.offset; + break; + + case MENU_ATTRIBUTE_TAG: + item->tag = value.offset; + break; + + default: + sciprintf("Attempt to set invalid attribute of menu %d, item %d: 0x%04x\n", + menu_nr, item_nr, attribute); + return 1; + } + + return 0; +} + +reg_t +menubar_get_attribute(state_t *s, int menu_nr, int item_nr, int attribute) +{ + menubar_t *menubar = s->menubar; + menu_item_t *item; + + if ((menu_nr < 0) || (item_nr < 0)) + return make_reg(0, -1); + + if ((menu_nr >= menubar->menus_nr) || (item_nr >= menubar->menus[menu_nr].items_nr)) + return make_reg(0, -1); + + item = menubar->menus[menu_nr].items + item_nr; + + switch (attribute) { + case MENU_ATTRIBUTE_SAID: + return item->said_pos; + + case MENU_ATTRIBUTE_TEXT: + return item->text_pos; + + case MENU_ATTRIBUTE_KEY: + return make_reg(0, item->key); + + case MENU_ATTRIBUTE_ENABLED: + return make_reg(0, item->enabled); + + case MENU_ATTRIBUTE_TAG: + return make_reg(0, item->tag); + + default: + sciprintf("Attempt to read invalid attribute from menu %d, item %d: 0x%04x\n", + menu_nr, item_nr, attribute); + return make_reg(0, -1); + } +} + +int +menubar_item_valid(state_t *s, int menu_nr, int item_nr) +{ + menubar_t *menubar = s->menubar; + menu_item_t *item; + + if ((menu_nr < 0) || (item_nr < 0)) + return 0; + + if ((menu_nr >= menubar->menus_nr) || (item_nr >= menubar->menus[menu_nr].items_nr)) + return 0; + + item = menubar->menus[menu_nr].items + item_nr; + + if ((item->type == MENU_TYPE_NORMAL) + && item->enabled) + return 1; + + return 0; /* May not be selected */ +} + + +int +menubar_map_pointer(state_t *s, int *menu_nr, int *item_nr, gfxw_port_t *port) +{ + menubar_t *menubar = s->menubar; + menu_t *menu; + + if (s->gfx_state->pointer_pos.y <= 10) { /* Re-evaulate menu */ + int x = MENU_LEFT_BORDER; + int i; + + for (i = 0; i < menubar->menus_nr; i++) { + int newx = x + MENU_BORDER_SIZE * 2 + menubar->menus[i].title_width; + + if (s->gfx_state->pointer_pos.x < x) + return 0; + + if (s->gfx_state->pointer_pos.x < newx) { + *menu_nr = i; + *item_nr = -1; + } + + x = newx; + } + + return 0; + + } else { + int row = (s->gfx_state->pointer_pos.y / 10) - 1; + + if ((*menu_nr < 0) || (*menu_nr >= menubar->menus_nr)) + return 1; /* No menu */ + else + menu = menubar->menus + *menu_nr; /* Menu is valid, assume that it's popped up */ + + if (menu->items_nr <= row) + return 1; + + if ((s->gfx_state->pointer_pos.x < port->bounds.x) || (s->gfx_state->pointer_pos.x > port->bounds.x + port->bounds.xl)) + return 1; + + if (menubar_item_valid(s, *menu_nr, row)) + *item_nr = row; /* Only modify if we'll be hitting a valid element */ + + return 0; + } + +} diff --git a/engines/sci/gfx/operations.c b/engines/sci/gfx/operations.c new file mode 100644 index 0000000000..a19e0bc99f --- /dev/null +++ b/engines/sci/gfx/operations.c @@ -0,0 +1,2490 @@ +/*************************************************************************** + gfx_operations Copyright (C) 2000 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Graphical operations, called from the widget state manager */ + +#include +#include + +#include + +#define PRECISE_PRIORITY_MAP /* Duplicate all operations on the local priority map as appropriate */ + +#undef GFXW_DEBUG_DIRTY /* Enable to debug stuff relevant for dirty rects + ** in widget management */ + +#ifdef GFXW_DEBUG_DIRTY +# define DDIRTY fprintf(stderr, "%s:%5d| ", __FILE__, __LINE__); fprintf +#else +# define DDIRTY if (0) fprintf +#endif + +/* Default color maps */ +#define DEFAULT_COLORS_NR 16 +gfx_pixmap_color_t default_colors[DEFAULT_COLORS_NR] = + {{GFX_COLOR_SYSTEM, 0x00, 0x00, 0x00}, {GFX_COLOR_SYSTEM, 0x00, 0x00, 0xaa}, + {GFX_COLOR_SYSTEM, 0x00, 0xaa, 0x00}, {GFX_COLOR_SYSTEM, 0x00, 0xaa, 0xaa}, + {GFX_COLOR_SYSTEM, 0xaa, 0x00, 0x00}, {GFX_COLOR_SYSTEM, 0xaa, 0x00, 0xaa}, + {GFX_COLOR_SYSTEM, 0xaa, 0x55, 0x00}, {GFX_COLOR_SYSTEM, 0xaa, 0xaa, 0xaa}, + {GFX_COLOR_SYSTEM, 0x55, 0x55, 0x55}, {GFX_COLOR_SYSTEM, 0x55, 0x55, 0xff}, + {GFX_COLOR_SYSTEM, 0x55, 0xff, 0x55}, {GFX_COLOR_SYSTEM, 0x55, 0xff, 0xff}, + {GFX_COLOR_SYSTEM, 0xff, 0x55, 0x55}, {GFX_COLOR_SYSTEM, 0xff, 0x55, 0xff}, + {GFX_COLOR_SYSTEM, 0xff, 0xff, 0x55}, {GFX_COLOR_SYSTEM, 0xff, 0xff, 0xff}}; /* "Normal" EGA */ + + +#define POINTER_VISIBLE_BUT_CLIPPED 2 + +/* Performs basic checks that apply to most functions */ +#define BASIC_CHECKS(error_retval) \ +if (!state) { \ + GFXERROR("Null state!\n"); \ + return error_retval; \ +} \ +if (!state->driver) { \ + GFXERROR("GFX driver invalid!\n"); \ + return error_retval; \ +} + +/* How to determine whether colors have to be allocated */ +#define PALETTE_MODE state->driver->mode->palette + +#define DRAW_POINTER { int __x = _gfxop_draw_pointer(state); if (__x) { GFXERROR("Drawing the mouse pointer failed!\n"); return __x;} } +#define REMOVE_POINTER { int __x = _gfxop_remove_pointer(state); if (__x) { GFXERROR("Removing the mouse pointer failed!\n"); return __x;} } + +/* #define GFXOP_DEBUG_DIRTY */ + +/* Internal operations */ + +static void +_gfxop_scale_rect(rect_t *rect, gfx_mode_t *mode) +{ + int xfact = mode->xfact; + int yfact = mode->yfact; + + rect->x *= xfact; + rect->y *= yfact; + rect->xl *= xfact; + rect->yl *= yfact; +} + +static void +_gfxop_scale_point(point_t *point, gfx_mode_t *mode) +{ + int xfact = mode->xfact; + int yfact = mode->yfact; + + point->x *= xfact; + point->y *= yfact; +} + +static void +_gfxop_alloc_colors(gfx_state_t *state, gfx_pixmap_color_t *colors, int colors_nr) +{ + int i; + + if (!PALETTE_MODE) + return; + + for (i = 0; i < colors_nr; i++) + gfx_alloc_color(state->driver->mode->palette, colors + i); +} + +static void +_gfxop_free_colors(gfx_state_t *state, gfx_pixmap_color_t *colors, int colors_nr) +{ + int i; + + if (!PALETTE_MODE) + return; + + for (i = 0; i < colors_nr; i++) + gfx_free_color(state->driver->mode->palette, colors + i); +} + + +int _gfxop_clip(rect_t *rect, rect_t clipzone) +/* Returns 1 if nothing is left */ +{ +#if 0 + printf ("Clipping (%d, %d) size (%d, %d) by (%d,%d)(%d,%d)\n", rect->x, rect->y, rect->xl, rect->yl, + clipzone.x, clipzone.y, clipzone.xl, clipzone.yl); +#endif + + if (rect->x < clipzone.x) { + rect->xl -= (clipzone.x - rect->x); + rect->x = clipzone.x; + } + + if (rect->y < clipzone.y) { + rect->yl -= (clipzone.y - rect->y); + rect->y = clipzone.y; + } + + if (rect->x + rect->xl > clipzone.x + clipzone.xl) + rect->xl = (clipzone.x + clipzone.xl) - rect->x; + + if (rect->y + rect->yl > clipzone.y + clipzone.yl) + rect->yl = (clipzone.y + clipzone.yl) - rect->y; + + if (rect->xl < 0) + rect->xl = 0; + if (rect->yl < 0) + rect->yl = 0; + +#if 0 + printf (" => (%d, %d) size (%d, %d)\n", rect->x, rect->y, rect->xl, rect->yl); +#endif + return (rect->xl <= 0 || rect->yl <= 0); +} + +static int +_gfxop_grab_pixmap(gfx_state_t *state, gfx_pixmap_t **pxmp, int x, int y, + int xl, int yl, int priority, rect_t *zone) + /* Returns 1 if the resulting data size was zero, GFX_OK or an error code otherwise */ +{ + int xfact = state->driver->mode->xfact; + int yfact = state->driver->mode->yfact; + int unscaled_xl = (xl + xfact - 1) / xfact; + int unscaled_yl = (yl + yfact - 1) / yfact; + *zone = gfx_rect(x, y, xl, yl); + + if (_gfxop_clip(zone, gfx_rect(0, 0, + 320 * state->driver->mode->xfact, + 200 * state->driver->mode->yfact))) + return GFX_ERROR; + + if (!*pxmp) + *pxmp = gfx_new_pixmap(unscaled_xl, unscaled_yl, GFX_RESID_NONE, 0, 0); + else + if (xl * yl > (*pxmp)->xl * (*pxmp)->yl) { + gfx_pixmap_free_data(*pxmp); + (*pxmp)->data = NULL; + } + + if (!(*pxmp)->data) { + (*pxmp)->index_xl = unscaled_xl + 1; + (*pxmp)->index_yl = unscaled_yl + 1; + gfx_pixmap_alloc_data(*pxmp, state->driver->mode); + } + return state->driver->grab_pixmap(state->driver, *zone, *pxmp, + priority? GFX_MASK_PRIORITY : GFX_MASK_VISUAL); +} + + +#define DRAW_LOOP(condition) \ +{ \ + rect_t drawrect = gfx_rect(pos.x, pos.y, pxm->index_xl, pxm->index_yl); \ + int offset, base_offset; \ + int read_offset, base_read_offset; \ + int x,y; \ + \ + if (!pxm->index_data) { \ + GFXERROR("Attempt to draw control color %d on pixmap %d/%d/%d without index data!\n", \ + color, pxm->ID, pxm->loop, pxm->cel); \ + return; \ + } \ + \ + if (_gfxop_clip(&drawrect, gfx_rect(0, 0, 320, 200))) \ + return; \ + \ + offset = base_offset = drawrect.x + drawrect.y * 320; \ + read_offset = base_read_offset = (drawrect.x - pos.x) + ((drawrect.y - pos.y) * pxm->index_xl); \ + \ + for (y = 0; y < drawrect.yl; y++) { \ + for (x = 0; x < drawrect.xl; x++) \ + if (pxm->index_data[read_offset++] != pxm->color_key) { \ + if (condition) \ + map->index_data[offset++] = color; \ + else \ + ++offset; \ + } else ++offset; \ + \ + offset = base_offset += 320; \ + read_offset = base_read_offset += pxm->index_xl; \ + } \ +} + +static void +_gfxop_draw_control(gfx_pixmap_t *map, gfx_pixmap_t *pxm, int color, point_t pos) +DRAW_LOOP(1) /* Always draw */ + +#ifdef PRECISE_PRIORITY_MAP +static void +_gfxop_draw_priority(gfx_pixmap_t *map, gfx_pixmap_t *pxm, int color, point_t pos) +DRAW_LOOP(map->index_data[offset] < color) /* Draw only lower priority */ +#endif + +#undef DRAW_LOOP + +static int +_gfxop_install_pixmap(gfx_driver_t *driver, gfx_pixmap_t *pxm) +{ + int error; + + if (driver->capabilities & GFX_CAPABILITY_PIXMAP_REGISTRY + && !(pxm->flags & GFX_PIXMAP_FLAG_INSTALLED)) { + error = driver->register_pixmap(driver, pxm); + + if (error) { + GFXERROR("driver->register_pixmap() returned error!\n"); + return error; + } + pxm->flags |= GFX_PIXMAP_FLAG_INSTALLED; + } + + if (driver->mode->palette && + (!(pxm->flags & GFX_PIXMAP_FLAG_PALETTE_SET))) { + int i; + int error; + + for (i = 0; i < pxm->colors_nr; i++) { + if ((error = driver->set_palette(driver, pxm->colors[i].global_index, + pxm->colors[i].r, + pxm->colors[i].g, + pxm->colors[i].b))) { + + GFXWARN("driver->set_palette(%d, %02x/%02x/%02x) failed!\n", + pxm->colors[i].global_index, + pxm->colors[i].r, + pxm->colors[i].g, + pxm->colors[i].b); + + if (error == GFX_FATAL) + return GFX_FATAL; + } + } + + pxm->flags |= GFX_PIXMAP_FLAG_PALETTE_SET; + } + return GFX_OK; +} + +static int +_gfxop_draw_pixmap(gfx_driver_t *driver, gfx_pixmap_t *pxm, int priority, int control, + rect_t src, rect_t dest, rect_t clip, int static_buf, gfx_pixmap_t *control_map, + gfx_pixmap_t *priority_map) +{ + int error; + rect_t clipped_dest = gfx_rect(dest.x, dest.y, dest.xl, dest.yl); + + if (control >= 0 || priority >= 0) { + point_t original_pos = gfx_point(dest.x / driver->mode->xfact, + dest.y / driver->mode->yfact); + + if (control >= 0) + _gfxop_draw_control(control_map, pxm, control, original_pos); + +#ifdef PRECISE_PRIORITY_MAP + if (priority >= 0) + _gfxop_draw_priority(priority_map, pxm, priority, original_pos); +#endif + } + + + if (_gfxop_clip(&clipped_dest, clip)) + return GFX_OK; + + src.x += clipped_dest.x - dest.x; + src.y += clipped_dest.y - dest.y; + src.xl = clipped_dest.xl; + src.yl = clipped_dest.yl; + + error = _gfxop_install_pixmap(driver, pxm); + if (error) return error; + + DDIRTY(stderr, "\\-> Drawing to actual %d %d %d %d\n", + clipped_dest.x / driver->mode->xfact, + clipped_dest.y / driver->mode->yfact, + clipped_dest.xl / driver->mode->xfact, + clipped_dest.yl / driver->mode->yfact); + + error = driver->draw_pixmap(driver, pxm, priority, src, clipped_dest, + static_buf? GFX_BUFFER_STATIC : GFX_BUFFER_BACK); + + if (error) { + GFXERROR("driver->draw_pixmap() returned error!\n"); + return error; + } + return GFX_OK; +} + +static int +_gfxop_remove_pointer(gfx_state_t *state) +{ + if (state->mouse_pointer_visible + && !state->mouse_pointer_in_hw + && state->mouse_pointer_bg) { + int retval; + + if (state->mouse_pointer_visible == POINTER_VISIBLE_BUT_CLIPPED) { + state->mouse_pointer_visible = 0; + state->pointer_pos.x = state->driver->pointer_x / state->driver->mode->xfact; + state->pointer_pos.y = state->driver->pointer_y / state->driver->mode->yfact; + return GFX_OK; + } + + state->mouse_pointer_visible = 0; + + retval = state->driver->draw_pixmap(state->driver, state->mouse_pointer_bg, GFX_NO_PRIORITY, + gfx_rect(0, 0, state->mouse_pointer_bg->xl, state->mouse_pointer_bg->yl), + state->pointer_bg_zone, + GFX_BUFFER_BACK); + + state->pointer_pos.x = state->driver->pointer_x / state->driver->mode->xfact; + state->pointer_pos.y = state->driver->pointer_y / state->driver->mode->yfact; + + return retval; + + } else { + state->pointer_pos.x = state->driver->pointer_x / state->driver->mode->xfact; + state->pointer_pos.y = state->driver->pointer_y / state->driver->mode->yfact; + return GFX_OK; + } +} + +static int /* returns 1 if there are no pointer bounds, 0 otherwise */ +_gfxop_get_pointer_bounds(gfx_state_t *state, rect_t *rect) +{ + gfx_pixmap_t *ppxm = state->mouse_pointer; + + if (!ppxm) + return 1; + + rect->x = state->driver->pointer_x - ppxm->xoffset * (state->driver->mode->xfact); + rect->y = state->driver->pointer_y - ppxm->yoffset * (state->driver->mode->yfact); + rect->xl = ppxm->xl; + rect->yl = ppxm->yl; + + return (_gfxop_clip(rect, gfx_rect(0, 0, 320 * state->driver->mode->xfact, + 200 * state->driver->mode->yfact))); +} + +static int +_gfxop_buffer_propagate_box(gfx_state_t *state, rect_t box, gfx_buffer_t buffer); + +static int +_gfxop_draw_pointer(gfx_state_t *state) +{ + if (state->mouse_pointer_visible || !state->mouse_pointer || state->mouse_pointer_in_hw) + return GFX_OK; + else { + int retval; + gfx_pixmap_t *ppxm = state->mouse_pointer; + int xfact, yfact; + int x = state->driver->pointer_x - ppxm->xoffset * (xfact = state->driver->mode->xfact); + int y = state->driver->pointer_y - ppxm->yoffset * (yfact = state->driver->mode->yfact); + int error; + + state->mouse_pointer_visible = 1; + + state->old_pointer_draw_pos.x = x; + state->old_pointer_draw_pos.y = y; + + /* FIXME: we are leaking the mouse_pointer_bg, but freeing it causes weirdness in jones + * we should reuse the buffer instead of malloc/free for better performance */ + + retval = _gfxop_grab_pixmap(state, &(state->mouse_pointer_bg), x, y, + ppxm->xl, ppxm->yl, 0, + &(state->pointer_bg_zone)); + + if (retval == GFX_ERROR) { + state->pointer_bg_zone = gfx_rect(0, 0, 320, 200); + state->mouse_pointer_visible = POINTER_VISIBLE_BUT_CLIPPED; + return GFX_OK; + } + + if (retval) + return retval; + + error = _gfxop_draw_pixmap(state->driver, ppxm, -1, -1, + gfx_rect(0, 0, ppxm->xl, ppxm->yl), + gfx_rect(x, y, ppxm->xl, ppxm->yl), + gfx_rect(0, 0, xfact * 320 , yfact * 200), + 0, state->control_map, state->priority_map); + + if (error) + return error; + + + return GFX_OK; + } +} + +gfx_pixmap_t * +_gfxr_get_cel(gfx_state_t *state, int nr, int *loop, int *cel, int palette) +{ + gfxr_view_t *view = gfxr_get_view(state->resstate, nr, loop, cel, palette); + gfxr_loop_t *indexed_loop; + + if (!view) + return NULL; + + if (*loop >= view->loops_nr + || *loop < 0) { + GFXWARN("Attempt to get cel from loop %d/%d inside view %d\n", *loop, + view->loops_nr, nr); + return NULL; + } + indexed_loop = view->loops + *loop; + + if (*cel >= indexed_loop->cels_nr + || *cel < 0) { + GFXWARN("Attempt to get cel %d/%d from view %d/%d\n", *cel, indexed_loop->cels_nr, + nr, *loop); + return NULL; + } + + return indexed_loop->cels[*cel]; /* Yes, view->cels uses a malloced pointer list. */ +} + +/*** Dirty rectangle operations ***/ + +static inline int +_gfxop_update_box(gfx_state_t *state, rect_t box) +{ + int retval; + _gfxop_scale_rect(&box, state->driver->mode); + + if ((retval = _gfxop_buffer_propagate_box(state, box, GFX_BUFFER_FRONT))) { + GFXERROR("Error occured while propagating box (%d,%d,%d,%d) to front buffer\n", + box.x, box.y, box.xl, box.yl); + return retval; + } + return GFX_OK; +} + + +static struct _dirty_rect * +_rect_create(rect_t box) +{ + struct _dirty_rect *rect; + + rect = (struct _dirty_rect*)sci_malloc(sizeof(struct _dirty_rect)); + rect->next = NULL; + rect->rect = box; + + return rect; +} + + +gfx_dirty_rect_t * +gfxdr_add_dirty(gfx_dirty_rect_t *base, rect_t box, int strategy) +{ + if (box.xl < 0) { + box.x += box.xl; + box.xl = - box.xl; + } + + if (box.yl < 0) { + box.y += box.yl; + box.yl = - box.yl; + } +#ifdef GFXOP_DEBUG_DIRTY + fprintf(stderr, "Adding new dirty (%d %d %d %d)\n", + GFX_PRINT_RECT(box)); +#endif + if (_gfxop_clip(&box, gfx_rect(0, 0, 320, 200))) + return base; + + switch (strategy) { + + case GFXOP_DIRTY_FRAMES_ONE: + if (base) + base->rect = gfx_rects_merge(box, base->rect); + else + base = _rect_create(box); + break; + + case GFXOP_DIRTY_FRAMES_CLUSTERS: { + struct _dirty_rect **rectp = &(base); + + while (*rectp) { + if (gfx_rects_overlap((*rectp)->rect, box)) { + struct _dirty_rect *next = (*rectp)->next; + box = gfx_rects_merge((*rectp)->rect, box); + free(*rectp); + *rectp = next; + } else + rectp = &((*rectp)->next); + } + *rectp = _rect_create(box); + + } break; + + default: + GFXERROR("Attempt to use invalid dirty frame mode %d!\nPlease refer to gfx_options.h.", strategy); + + } + + return base; +} + +static void +_gfxop_add_dirty(gfx_state_t *state, rect_t box) +{ + if (state->disable_dirty) + return; + + state->dirty_rects = gfxdr_add_dirty(state->dirty_rects, box, state->options->dirty_frames); +} + +static inline void +_gfxop_add_dirty_x(gfx_state_t *state, rect_t box) + /* Extends the box size by one before adding (used for lines) */ +{ + if (box.xl < 0) + box.xl--; + else + box.xl++; + + if (box.yl < 0) + box.yl--; + else + box.yl++; + + _gfxop_add_dirty(state, box); +} + +static int +_gfxop_clear_dirty_rec(gfx_state_t *state, struct _dirty_rect *rect) +{ + int retval; + + if (!rect) + return GFX_OK; + +#ifdef GFXOP_DEBUG_DIRTY + fprintf(stderr, "\tClearing dirty (%d %d %d %d)\n", + GFX_PRINT_RECT(rect->rect)); +#endif + if (!state->fullscreen_override) + retval = _gfxop_update_box(state, rect->rect); + else + retval = GFX_OK; + + retval |= _gfxop_clear_dirty_rec(state, rect->next); + + free(rect); + return retval; +} + + +/*** Exported operations ***/ + +static void +init_aux_pixmap(gfx_pixmap_t **pixmap) +{ + *pixmap = gfx_pixmap_alloc_index_data(gfx_new_pixmap(320, 200, GFX_RESID_NONE, 0, 0)); + (*pixmap)->flags |= GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + (*pixmap)->colors_nr = DEFAULT_COLORS_NR; + (*pixmap)->colors = default_colors; +} + +static int +_gfxop_init_common(gfx_state_t *state, gfx_options_t *options, void *misc_payload) +{ + state->options = options; + + if (!((state->resstate = gfxr_new_resource_manager(state->version, + state->options, + state->driver, + misc_payload)))) { + GFXERROR("Failed to initialize resource manager!\n"); + return GFX_FATAL; + } + + if ((state->static_palette = + gfxr_interpreter_get_static_palette(state->resstate, + state->version, + &(state->static_palette_entries), + misc_payload))) + _gfxop_alloc_colors(state, state->static_palette, state->static_palette_entries); + + state->visible_map = GFX_MASK_VISUAL; + state->fullscreen_override = NULL; /* No magical override */ + gfxop_set_clip_zone(state, gfx_rect(0, 0, 320, 200)); + + state->mouse_pointer = state->mouse_pointer_bg = NULL; + state->mouse_pointer_visible = 0; + + init_aux_pixmap(&(state->control_map)); + init_aux_pixmap(&(state->priority_map)); + init_aux_pixmap(&(state->static_priority_map)); + + state->options = options; + state->mouse_pointer_in_hw = 0; + state->disable_dirty = 0; + state->events = NULL; + + state->pic = state->pic_unscaled = NULL; + + state->pic_nr = -1; /* Set background pic number to an invalid value */ + + state->tag_mode = 0; + + state->dirty_rects = NULL; + + + return GFX_OK; +} + +int +gfxop_init_default(gfx_state_t *state, gfx_options_t *options, void *misc_info) +{ + BASIC_CHECKS(GFX_FATAL); + if (state->driver->init(state->driver)) + return GFX_FATAL; + + return _gfxop_init_common(state, options, misc_info); +} + + +int +gfxop_init(gfx_state_t *state, int xfact, int yfact, gfx_color_mode_t bpp, + gfx_options_t *options, void *misc_info) +{ + int color_depth = bpp? bpp : 1; + int initialized = 0; + + BASIC_CHECKS(GFX_FATAL); + + do { + if (!state->driver->init_specific(state->driver, xfact, yfact, color_depth)) + initialized = 1; + else + color_depth++; + } while (!initialized && color_depth < 9 && !bpp); + + if (!initialized) + return GFX_FATAL; + + return _gfxop_init_common(state, options, misc_info); +} + + +int +gfxop_set_parameter(gfx_state_t *state, char *attribute, char *value) +{ + BASIC_CHECKS(GFX_FATAL); + + return state->driver->set_parameter(state->driver, attribute, value); +} + + +int +gfxop_exit(gfx_state_t *state) +{ + BASIC_CHECKS(GFX_ERROR); + gfxr_free_resource_manager(state->driver, state->resstate); + + if (state->control_map) { + gfx_free_pixmap(state->driver, state->control_map); + state->control_map = NULL; + } + + if (state->priority_map) { + gfx_free_pixmap(state->driver, state->priority_map); + state->priority_map = NULL; + } + + if (state->static_priority_map) { + gfx_free_pixmap(state->driver, state->static_priority_map); + state->static_priority_map = NULL; + } + + if (state->mouse_pointer_bg) { + gfx_free_pixmap(state->driver, state->mouse_pointer_bg); + state->mouse_pointer_bg = NULL; + } + + state->driver->exit(state->driver); + return GFX_OK; +} + + +int +gfxop_have_mouse(gfx_state_t *state) +{ + return state->driver->capabilities & GFX_CAPABILITY_MOUSE_SUPPORT; +} + + +static int +_gfxop_scan_one_bitmask(gfx_pixmap_t *pixmap, rect_t zone) +{ + int retval = 0; + int pixmap_xscale = pixmap->index_xl / 320; + int pixmap_yscale = pixmap->index_yl / 200; + int line_width = pixmap_yscale * pixmap->index_xl; + int startindex = (line_width * zone.y) + (zone.x * pixmap_xscale); + + startindex += pixmap_xscale >> 1; /* Center on X */ + startindex += (pixmap_yscale >> 1) * pixmap->index_xl; /* Center on Y */ + + if (_gfxop_clip(&zone, gfx_rect(0, 0, pixmap->index_xl, pixmap->index_yl))) + return 0; + + while (zone.yl--) { + int i; + for (i = 0; i < (zone.xl * pixmap_xscale); i += pixmap_xscale) + retval |= (1 << ((pixmap->index_data[startindex + i]) & 0xf)); + + startindex += line_width; + } + + return retval; +} + +int +gfxop_scan_bitmask(gfx_state_t *state, rect_t area, gfx_map_mask_t map) +{ + gfxr_pic_t *pic = (state->pic_unscaled)? state->pic_unscaled : state->pic; + int retval = 0; + + _gfxop_clip(&area, gfx_rect(0, 10, 320, 200)); + + if (area.xl <= 0 + || area.yl <= 0) + return 0; + + if (map & GFX_MASK_VISUAL) + retval |= _gfxop_scan_one_bitmask(pic->visual_map, area); + + if (map & GFX_MASK_PRIORITY) + retval |= _gfxop_scan_one_bitmask(state->priority_map, area); + if (map & GFX_MASK_CONTROL) + retval |= _gfxop_scan_one_bitmask(state->control_map, area); + + return retval; +} + + +#define MIN_X 0 +#define MIN_Y 0 +#define MAX_X 319 +#define MAX_Y 199 + +int +gfxop_set_clip_zone(gfx_state_t *state, rect_t zone) +{ + int xfact, yfact; + BASIC_CHECKS(GFX_ERROR); + + DDIRTY(stderr, "-- Setting clip zone %d %d %d %d\n", GFX_PRINT_RECT(zone)); + + xfact = state->driver->mode->xfact; + yfact = state->driver->mode->yfact; + + if (zone.x < MIN_X) { + zone.xl -= (zone.x - MIN_X); + zone.x = MIN_X; + } + + if (zone.y < MIN_Y) { + zone.yl -= (zone.y - MIN_Y); + zone.y = MIN_Y; + } + + if (zone.x + zone.xl > MAX_X) + zone.xl = MAX_X + 1 - zone.x; + + if (zone.y + zone.yl > MAX_Y) + zone.yl = MAX_Y + 1 - zone.y; + + memcpy(&(state->clip_zone_unscaled), &zone, sizeof(rect_t)); + + state->clip_zone.x = state->clip_zone_unscaled.x * xfact; + state->clip_zone.y = state->clip_zone_unscaled.y * yfact; + state->clip_zone.xl = state->clip_zone_unscaled.xl * xfact; + state->clip_zone.yl = state->clip_zone_unscaled.yl * yfact; + + return GFX_OK; +} + +int +gfxop_set_color(gfx_state_t *state, gfx_color_t *color, int r, int g, int b, int a, + int priority, int control) +{ + gfx_pixmap_color_t pixmap_color = {0}; + int error_code; + int mask = ((r >= 0 && g >= 0 && b >= 0) ? GFX_MASK_VISUAL : 0) + | ((priority >= 0)? GFX_MASK_PRIORITY : 0) + | ((control >= 0)? GFX_MASK_CONTROL : 0); + + BASIC_CHECKS(GFX_FATAL); + + if (PALETTE_MODE && a >= GFXOP_ALPHA_THRESHOLD) + mask &= ~GFX_MASK_VISUAL; + + color->mask = mask; + + color->priority = priority; + color->control = control; + + if (mask & GFX_MASK_VISUAL) { + + color->visual.r = r; + color->visual.g = g; + color->visual.b = b; + color->alpha = a; + + if (PALETTE_MODE) { + pixmap_color.r = r; + pixmap_color.g = g; + pixmap_color.b = b; + pixmap_color.global_index = GFX_COLOR_INDEX_UNMAPPED; + if ((error_code = gfx_alloc_color(state->driver->mode->palette, &pixmap_color))) { + if (error_code < 0) { + GFXWARN("Could not get color entry for %02x/%02x/%02x\n", r, g, b); + return error_code; + } else if ((error_code = state->driver->set_palette(state->driver, pixmap_color.global_index, (byte) r, (byte) g, (byte) b))) { + GFXWARN("Graphics driver failed to set color index %d to (%02x/%02x/%02x)\n", + pixmap_color.global_index, r, g, b); + return error_code; + } + } + color->visual.global_index = pixmap_color.global_index; + } + } + return GFX_OK; +} + +int +gfxop_set_system_color(gfx_state_t *state, gfx_color_t *color) +{ + gfx_palette_color_t *palette_colors; + BASIC_CHECKS(GFX_FATAL); + + if (!PALETTE_MODE) + return GFX_OK; + + if (color->visual.global_index < 0 + || color->visual.global_index >= state->driver->mode->palette->max_colors_nr) { + GFXERROR("Attempt to set invalid color index %02x as system color\n", color->visual.global_index); + return GFX_ERROR; + } + + palette_colors = state->driver->mode->palette->colors; + palette_colors[color->visual.global_index].lockers = GFX_COLOR_SYSTEM; + + return GFX_OK; +} + +int +gfxop_free_color(gfx_state_t *state, gfx_color_t *color) +{ + gfx_palette_color_t *palette_color = {0}; + gfx_pixmap_color_t pixmap_color = {0}; + int error_code; + BASIC_CHECKS(GFX_FATAL); + + if (!PALETTE_MODE) + return GFX_OK; + + if (color->visual.global_index < 0 + || color->visual.global_index >= state->driver->mode->palette->max_colors_nr) { + GFXERROR("Attempt to free invalid color index %02x\n", color->visual.global_index); + return GFX_ERROR; + } + + pixmap_color.global_index = color->visual.global_index; + palette_color = state->driver->mode->palette->colors + pixmap_color.global_index; + pixmap_color.r = palette_color->r; + pixmap_color.g = palette_color->g; + pixmap_color.b = palette_color->b; + + if ((error_code = gfx_free_color(state->driver->mode->palette, &pixmap_color))) { + GFXWARN("Failed to free color with color index %02x\n", color->visual.global_index); + return error_code; + } + + return GFX_OK; +} + +/******************************/ +/* Generic drawing operations */ +/******************************/ + + +static int +line_check_bar(int *start, int *length, int clipstart, int cliplength) +{ + int overlength; + + if (*start < clipstart) { + *length -= (clipstart - *start); + *start = clipstart; + } + + overlength = 1 + (*start + *length) - (clipstart + cliplength); + + if (overlength > 0) + *length -= overlength; + + return (*length < 0); +} + +static void +clip_line_partial(float *start, float *end, float delta_val, float pos_val, float start_val, float end_val) +{ + float my_start = (start_val - pos_val) * delta_val; + float my_end = (end_val - pos_val) * delta_val; + + if (my_end < *end) + *end = my_end; + if (my_start > *start) + *start = my_start; +} + +static int +line_clip(rect_t *line, rect_t clip, int xfact, int yfact) +/* returns 1 if nothing is left, or 0 if part of the line is in the clip window */ +{ + /* Compensate for line thickness (should match precisely) */ + clip.xl -= xfact; + clip.yl -= yfact; + + if (!line->xl) {/* vbar */ + if (line->x < clip.x || line->x >= (clip.x + clip.xl)) + return 1; + + return line_check_bar(&(line->y), &(line->yl), clip.y, clip.yl); + + } else + + if (!line->yl) {/* hbar */ + if (line->y < clip.y || line->y >= (clip.y + clip.yl)) + return 1; + + return line_check_bar(&(line->x), &(line->xl), clip.x, clip.xl); + + } else { /* "normal" line */ + float start = 0.0, end = 1.0; + float xv = (float) line->xl; + float yv = (float) line->yl; + + if (line->xl < 0) + clip_line_partial(&start, &end, (float) (1.0 / xv), (float) line->x, (float) (clip.x + clip.xl), (float) clip.x); + else + clip_line_partial(&start, &end, (float) (1.0 / xv), (float) line->x, (float) clip.x, (float) (clip.x + clip.xl)); + + if (line->yl < 0) + clip_line_partial(&start, &end, (float) (1.0 / yv), (float) line->y, (float) (clip.y + clip.yl), (float) clip.y); + else + clip_line_partial(&start, &end, (float) (1.0 / yv), (float) line->y, (float) clip.y, (float) (clip.y + clip.yl)); + + line->x += (int) (xv * start); + line->y += (int) (yv * start); + + line->xl = (int) (xv * (end-start)); + line->yl = (int) (yv * (end-start)); + + return (start > 1.0 || end < 0.0); + } + return 0; +} + +static int +point_clip(point_t *start, point_t *end, rect_t clip, int xfact, int yfact) +{ + rect_t line = gfx_rect(start->x, start->y, end->x - start->x, end->y - start->y); + int retval = line_clip(&line, clip, xfact, yfact); + + start->x = line.x; + start->y = line.y; + + end->x = line.x + line.xl; + end->y = line.y + line.yl; + + return retval; +} + + + +static void +draw_line_to_control_map(gfx_state_t *state, point_t start, point_t end, gfx_color_t color) +{ + if (color.mask & GFX_MASK_CONTROL) + if (!point_clip(&start, &end, state->clip_zone_unscaled, 0, 0)) + gfx_draw_line_pixmap_i(state->control_map, start, end, color.control); +} + +static int +simulate_stippled_line_draw(gfx_driver_t *driver, int skipone, point_t start, point_t end, gfx_color_t color, gfx_line_mode_t line_mode) + /* Draws a stippled line if this isn't supported by the driver (skipone is ignored ATM) */ +{ + int xl = end.x - start.x; + int yl = end.y - start.y; + int stepwidth = (xl)? driver->mode->xfact : driver->mode->yfact; + int dbl_stepwidth = 2*stepwidth; + int linelength = (line_mode == GFX_LINE_MODE_FINE)? stepwidth - 1 : 0; + int *posvar; + int length; + int delta; + int length_left; + + if (!xl) { /* xl = 0, so we move along yl */ + posvar = &start.y; + length = yl; + delta = (yl < 0)? -dbl_stepwidth : dbl_stepwidth; + } else { + assert (!yl); /* We don't do diagonals; that's not needed ATM. */ + posvar = &start.x; + length = xl; + delta = (xl < 0)? -dbl_stepwidth : dbl_stepwidth; + } + + length_left = length; + + if (skipone) { + length_left -= stepwidth; + *posvar += stepwidth; + } + + length /= delta; + + length_left -= length * dbl_stepwidth; + + if (xl) + xl = linelength; + else + yl = linelength; + + while (length--) { + int retval; + point_t nextpos = gfx_point(start.x + xl, start.y + yl); + + if ((retval = driver->draw_line(driver, start, nextpos, + color, line_mode, GFX_LINE_STYLE_NORMAL))) { + GFXERROR("Failed to draw partial stippled line (%d,%d) -- (%d,%d)\n", + GFX_PRINT_POINT(start), GFX_PRINT_POINT(nextpos)); + return retval; + } + *posvar += delta; + } + + if (length_left) { + int retval; + point_t nextpos; + + if (length_left > stepwidth) + length_left = stepwidth; + + if (xl) + xl = length_left; + else + if (yl) + yl = length_left; + + nextpos = gfx_point(start.x + xl, start.y + yl); + + if ((retval = driver->draw_line(driver, start, nextpos, color, line_mode, GFX_LINE_STYLE_NORMAL))) { + GFXERROR("Failed to draw partial stippled line (%d,%d) -- (%d,%d)\n", + GFX_PRINT_POINT(start), GFX_PRINT_POINT(nextpos)); + return retval; + } + } + + return GFX_OK; +} + + +static int +_gfxop_draw_line_clipped(gfx_state_t *state, point_t start, point_t end, gfx_color_t color, gfx_line_mode_t line_mode, + gfx_line_style_t line_style) +{ + int retval; + int skipone = (start.x ^ end.y) & 1; /* Used for simulated line stippling */ + + BASIC_CHECKS(GFX_FATAL); + REMOVE_POINTER; + + /* First, make sure that the line is normalized */ + if (start.y > end.y) { + point_t swap = start; + start = end; + end = swap; + } + + if (start.x < state->clip_zone.x + || start.y < state->clip_zone.y + || end.x >= (state->clip_zone.x + state->clip_zone.xl) + || end.y >= (state->clip_zone.y + state->clip_zone.yl)) + if (point_clip(&start, &end, state->clip_zone, state->driver->mode->xfact - 1, + state->driver->mode->yfact - 1)) + return GFX_OK; /* Clipped off */ + + if (line_style == GFX_LINE_STYLE_STIPPLED) { + if (start.x != end.x && start.y != end.y) { + GFXWARN("Attempt to draw stippled line which is neither an hbar nor a vbar: (%d,%d) -- (%d,%d)\n", + GFX_PRINT_POINT(start), GFX_PRINT_POINT(end)); + return GFX_ERROR; + } + if (!(state->driver->capabilities & GFX_CAPABILITY_STIPPLED_LINES)) + return simulate_stippled_line_draw(state->driver, skipone, start, end, color, line_mode); + } + + if (line_mode == GFX_LINE_MODE_FINE + && !(state->driver->capabilities & GFX_CAPABILITY_FINE_LINES)) + line_mode = GFX_LINE_MODE_FAST; + + if ((retval = state->driver->draw_line(state->driver, start, end, color, line_mode, line_style))) { + GFXERROR("Failed to draw line (%d,%d) -- (%d,%d)\n", + GFX_PRINT_POINT(start), GFX_PRINT_POINT(end)); + return retval; + } + return GFX_OK; +} + +int +gfxop_draw_line(gfx_state_t *state, point_t start, point_t end, + gfx_color_t color, gfx_line_mode_t line_mode, + gfx_line_style_t line_style) +{ + int xfact, yfact; + + BASIC_CHECKS(GFX_FATAL); + _gfxop_add_dirty_x(state, gfx_rect(start.x, start.y, end.x - start.x, end.y - start.y)); + + xfact = state->driver->mode->xfact; + yfact = state->driver->mode->yfact; + + draw_line_to_control_map(state, start, end, color); + + _gfxop_scale_point(&start, state->driver->mode); + _gfxop_scale_point(&end, state->driver->mode); + + if (line_mode == GFX_LINE_MODE_FINE) { + start.x += xfact >> 1; + start.y += yfact >> 1; + + end.x += xfact >> 1; + end.y += yfact >> 1; + } + + return _gfxop_draw_line_clipped(state, start, end, color, line_mode, line_style); +} + +int +gfxop_draw_rectangle(gfx_state_t *state, rect_t rect, gfx_color_t color, gfx_line_mode_t line_mode, + gfx_line_style_t line_style) +{ + int retval = 0; + int xfact, yfact; + int xunit, yunit; + int x, y, xl, yl; + point_t upper_left_u, upper_right_u, lower_left_u, lower_right_u; + point_t upper_left, upper_right, lower_left, lower_right; + + BASIC_CHECKS(GFX_FATAL); + REMOVE_POINTER; + + xfact = state->driver->mode->xfact; + yfact = state->driver->mode->yfact; + + if (line_mode == GFX_LINE_MODE_FINE + && state->driver->capabilities & GFX_CAPABILITY_FINE_LINES) { + xunit = yunit = 1; + xl = 1 + (rect.xl - 1) * xfact; + yl = 1 + (rect.yl - 1) * yfact; + x = rect.x * xfact + (xfact - 1); + y = rect.y * yfact + (yfact - 1); + } else { + xunit = xfact; + yunit = yfact; + xl = rect.xl * xfact; + yl = rect.yl * yfact; + x = rect.x * xfact; + y = rect.y * yfact; + } + + upper_left_u = gfx_point(rect.x, rect.y); + upper_right_u = gfx_point(rect.x + rect.xl, rect.y); + lower_left_u = gfx_point(rect.x, rect.y + rect.yl); + lower_right_u = gfx_point(rect.x + rect.xl, rect.y + rect.yl); + + upper_left = gfx_point(x, y); + upper_right = gfx_point(x + xl, y); + lower_left = gfx_point(x, y + yl); + lower_right = gfx_point(x + xl, y + yl); + +#define PARTIAL_LINE(pt1, pt2) \ + retval |= _gfxop_draw_line_clipped(state, pt1, pt2, color, line_mode, line_style); \ + draw_line_to_control_map(state, pt1##_u, pt2##_u, color); \ + _gfxop_add_dirty_x(state, \ + gfx_rect(pt1##_u.x, pt1##_u.y, pt2##_u.x - pt1##_u.x, pt2##_u.y - pt1##_u.y)) + + PARTIAL_LINE (upper_left, upper_right); + PARTIAL_LINE (upper_right, lower_right); + PARTIAL_LINE (lower_right, lower_left); + PARTIAL_LINE (lower_left, upper_left); + +#undef PARTIAL_LINE + if (retval) { + GFXERROR("Failed to draw rectangle (%d,%d)+(%d,%d)\n", rect.x, rect.y, rect.xl, rect.yl); + return retval; + } + return GFX_OK; +} + + +#define COLOR_MIX(type, dist) ((color1.type * dist) + (color2.type * (1.0 - dist))) + + +int +gfxop_draw_box(gfx_state_t *state, rect_t box, gfx_color_t color1, gfx_color_t color2, + gfx_box_shade_t shade_type) +{ + gfx_driver_t *drv = state->driver; + int reverse = 0; /* switch color1 and color2 */ + float mod_offset = 0.0, mod_breadth = 1.0; /* 0.0 to 1.0: Color adjustment */ + gfx_rectangle_fill_t driver_shade_type; + rect_t new_box; + gfx_color_t draw_color1, draw_color2 = {{0, 0, 0}, 0, 0, 0, 0}; + + BASIC_CHECKS(GFX_FATAL); + REMOVE_POINTER; + + if (PALETTE_MODE || !(state->driver->capabilities & GFX_CAPABILITY_SHADING)) + shade_type = GFX_BOX_SHADE_FLAT; + + + _gfxop_add_dirty(state, box); + + if (color1.mask & GFX_MASK_CONTROL) { + /* Write control block, clipped by 320x200 */ + memcpy(&new_box, &box, sizeof(rect_t)); + _gfxop_clip(&new_box, gfx_rect(0, 0, 320, 200)); + + gfx_draw_box_pixmap_i(state->control_map, new_box, color1.control); + } + + _gfxop_scale_rect(&box, state->driver->mode); + + if (!(color1.mask & (GFX_MASK_VISUAL | GFX_MASK_PRIORITY))) + return GFX_OK; /* So long... */ + + if (box.xl <= 1 || box.yl <= 1) { + GFXDEBUG("Attempt to draw box with size %dx%d\n", box.xl, box.yl); + return GFX_OK; + } + + memcpy(&new_box, &box, sizeof(rect_t)); + if (_gfxop_clip(&new_box, state->clip_zone)) + return GFX_OK; + + switch (shade_type) { + + case GFX_BOX_SHADE_FLAT: + driver_shade_type = GFX_SHADE_FLAT; + break; + + case GFX_BOX_SHADE_LEFT: reverse = 1; + case GFX_BOX_SHADE_RIGHT: + driver_shade_type = GFX_SHADE_HORIZONTALLY; + mod_offset = (float) (((new_box.x - box.x) * 1.0) / (box.xl * 1.0)); + mod_breadth = (float) ((new_box.xl * 1.0) / (box.xl * 1.0)); + break; + + case GFX_BOX_SHADE_UP: reverse = 1; + case GFX_BOX_SHADE_DOWN: + driver_shade_type = GFX_SHADE_VERTICALLY; + mod_offset = (float) (((new_box.y - box.y) * 1.0) / (box.yl * 1.0)); + mod_breadth = (float) ((new_box.yl * 1.0) / (box.yl * 1.0)); + break; + + default: + GFXERROR("Invalid shade type: %d\n", shade_type); + return GFX_ERROR; + } + + + if (reverse) + mod_offset = (float) (1.0 - (mod_offset + mod_breadth)); + /* Reverse offset if we have to interpret colors inversely */ + + if (shade_type == GFX_BOX_SHADE_FLAT) + return drv->draw_filled_rect(drv, new_box, color1, color1, GFX_SHADE_FLAT); + else { + if (PALETTE_MODE) { + GFXWARN("Attempting to draw shaded box in palette mode!\n"); + return GFX_ERROR; + } + + draw_color1.mask = draw_color2.mask = color1.mask; + draw_color1.priority = draw_color2.priority = color1.priority; + + if (draw_color1.mask & GFX_MASK_VISUAL) { + draw_color1.visual.r = (guint8) COLOR_MIX(visual.r, mod_offset); + draw_color1.visual.g = (guint8) COLOR_MIX(visual.g, mod_offset); + draw_color1.visual.b = (guint8) COLOR_MIX(visual.b, mod_offset); + draw_color1.alpha = (guint8) COLOR_MIX(alpha, mod_offset); + + mod_offset += mod_breadth; + + draw_color2.visual.r = (guint8) COLOR_MIX(visual.r, mod_offset); + draw_color2.visual.g = (guint8) COLOR_MIX(visual.g, mod_offset); + draw_color2.visual.b = (guint8) COLOR_MIX(visual.b, mod_offset); + draw_color2.alpha = (guint8) COLOR_MIX(alpha, mod_offset); + } + if (reverse) + return drv->draw_filled_rect(drv, new_box, draw_color2, draw_color1, driver_shade_type); + else + return drv->draw_filled_rect(drv, new_box, draw_color1, draw_color2, driver_shade_type); + } +} +#undef COLOR_MIX + + +int +gfxop_fill_box(gfx_state_t *state, rect_t box, gfx_color_t color) +{ + return gfxop_draw_box(state, box, color, color, GFX_BOX_SHADE_FLAT); +} + + + +static int +_gfxop_buffer_propagate_box(gfx_state_t *state, rect_t box, gfx_buffer_t buffer) +{ + int error; + + if (_gfxop_clip(&box, gfx_rect(0, 0, 320 * state->driver->mode->xfact, 200 * state->driver->mode->yfact))) + return GFX_OK; + + if ((error = state->driver->update(state->driver, box, gfx_point(box.x, box.y), buffer))) { + GFXERROR("Error occured while updating region (%d,%d,%d,%d) in buffer %d\n", + box.x, box.y, box.xl, box.yl, buffer); + return error; + } + return GFX_OK; +} + +extern int sci0_palette; +int +gfxop_clear_box(gfx_state_t *state, rect_t box) +{ + BASIC_CHECKS(GFX_FATAL); + REMOVE_POINTER; + _gfxop_add_dirty(state, box); + DDIRTY(stderr, "[] clearing box %d %d %d %d\n", GFX_PRINT_RECT(box)); + if (box.x == 29 + && box.y == 77 + && (sci0_palette == 1)) { + BREAKPOINT(); + } + + _gfxop_clip(&box, gfx_rect(0, 0, 320, 200)); +#ifdef PRECISE_PRIORITY_MAP + if (state->pic_unscaled) + gfx_copy_pixmap_box_i(state->priority_map, state->static_priority_map, box); +#endif + + _gfxop_scale_rect(&box, state->driver->mode); + + return _gfxop_buffer_propagate_box(state, box, GFX_BUFFER_BACK); +} + +int +gfxop_set_visible_map(gfx_state_t *state, gfx_map_mask_t visible_map) +{ + switch (visible_map) { + + case GFX_MASK_VISUAL: + state->fullscreen_override = NULL; + if (visible_map != state->visible_map) { + rect_t rect = gfx_rect(0, 0, 320, 200); + gfxop_clear_box(state, rect); + gfxop_update_box(state, rect); + } + break; + + case GFX_MASK_PRIORITY: + state->fullscreen_override = state->priority_map; + break; + + case GFX_MASK_CONTROL: + state->fullscreen_override = state->control_map; + break; + + default: + fprintf(stderr, "Invalid display map %d selected!\n", visible_map); + return GFX_ERROR; + } + + state->visible_map = visible_map; + return GFX_OK; +} + +int +gfxop_update(gfx_state_t *state) +{ + int retval; + + BASIC_CHECKS(GFX_FATAL); + DRAW_POINTER; + + retval = _gfxop_clear_dirty_rec(state, state->dirty_rects); + + state->dirty_rects = NULL; + + if (state->fullscreen_override) { + /* We've been asked to re-draw the active full-screen image, essentially. */ + rect_t rect = gfx_rect(0, 0, 320, 200); + gfx_xlate_pixmap(state->fullscreen_override, state->driver->mode, GFX_XLATE_FILTER_NONE); + gfxop_draw_pixmap(state, state->fullscreen_override, rect, gfx_point(0,0)); + retval |= _gfxop_update_box(state, rect); + } + + if (retval) { + GFXERROR("Clearing the dirty rectangles failed!\n"); + } + + if (state->tag_mode) { + /* This usually happens after a pic and all resources have been drawn */ + gfxr_free_tagged_resources(state->driver, state->resstate); + state->tag_mode = 0; + } + + return retval; +} + + +int +gfxop_update_box(gfx_state_t *state, rect_t box) +{ + BASIC_CHECKS(GFX_FATAL); + DRAW_POINTER; + + if (state->disable_dirty) + _gfxop_update_box(state, box); + else + _gfxop_add_dirty(state, box); + + return gfxop_update(state); +} + +int +gfxop_enable_dirty_frames(gfx_state_t *state) +{ + BASIC_CHECKS(GFX_ERROR); + state->disable_dirty = 0; + + return GFX_OK; +} + +int +gfxop_disable_dirty_frames(gfx_state_t *state) +{ + BASIC_CHECKS(GFX_ERROR); + + state->disable_dirty = 1; + + return GFX_OK; +} + + +/**********************/ +/* Pointer and IO ops */ +/**********************/ + +#define SECONDS_OF_DAY (24*60*60) +#define MILLION 1000000 +/* Sure, this may seem silly, but it's too easy to miss a zero...) */ + + +#define GFXOP_FULL_POINTER_REFRESH if (_gfxop_full_pointer_refresh(state)) { GFXERROR("Failed to do full pointer refresh!\n"); return GFX_ERROR; } + +static int +_gfxop_full_pointer_refresh(gfx_state_t *state) +{ + rect_t pointer_bounds; + rect_t old_pointer_bounds = {0, 0, 0, 0}; + int new_x = state->driver->pointer_x; + int new_y = state->driver->pointer_y; + + if (new_x != state->old_pointer_draw_pos.x + || new_y != state->old_pointer_draw_pos.y) { + point_t pp_new = gfx_point(new_x / state->driver->mode->xfact, + new_y / state->driver->mode->yfact); + + if (!_gfxop_get_pointer_bounds(state, &pointer_bounds)) { + memcpy(&old_pointer_bounds, &(state->pointer_bg_zone), sizeof(rect_t)); + REMOVE_POINTER; + state->pointer_pos = pp_new; + + DRAW_POINTER; + if (_gfxop_buffer_propagate_box(state, pointer_bounds, GFX_BUFFER_FRONT)) return 1; + if (_gfxop_buffer_propagate_box(state, old_pointer_bounds, GFX_BUFFER_FRONT)) return 1; + + state->old_pointer_draw_pos = gfx_point(new_x, new_y); + } else + state->pointer_pos = pp_new; + } + return 0; +} + +int +gfxop_usleep(gfx_state_t *state, long usecs) +{ + long time, utime; + long wakeup_time, wakeup_utime; + long add_seconds; + int retval = GFX_OK; + + BASIC_CHECKS(GFX_FATAL); + + sci_gettime(&wakeup_time, &wakeup_utime); + wakeup_utime += usecs; + + add_seconds = (wakeup_utime / MILLION); + wakeup_time += add_seconds; + wakeup_utime -= (MILLION * add_seconds); + + do { + GFXOP_FULL_POINTER_REFRESH; + sci_gettime(&time, &utime); + usecs = (wakeup_time - time) * MILLION + wakeup_utime - utime; + } while ((usecs > 0) && !(retval = state->driver->usec_sleep(state->driver, usecs))); + + if (retval) { + GFXWARN("Waiting failed\n"); + } + + return retval; +} + + +int +_gfxop_set_pointer(gfx_state_t *state, gfx_pixmap_t *pxm) +{ + rect_t old_pointer_bounds = {0}; + rect_t pointer_bounds = {0}; + int retval = -1; + int draw_old; + int draw_new = 0; + + BASIC_CHECKS(GFX_FATAL); + + draw_old = state->mouse_pointer != NULL; + + if (state->driver->capabilities & GFX_CAPABILITY_MOUSE_POINTER) { + + if (draw_old && state->mouse_pointer->colors_nr > 2) + draw_old = state->driver->capabilities & GFX_CAPABILITY_COLOR_MOUSE_POINTER; + + if (!draw_old + && state->mouse_pointer + && (state->driver->capabilities & GFX_CAPABILITY_POINTER_PIXMAP_REGISTRY)) + if ((retval = state->driver->unregister_pixmap(state->driver, state->mouse_pointer))){ + GFXERROR("Pointer un-registration failed!\n"); + return retval; + } + + if (pxm == NULL + || (state->driver->capabilities & GFX_CAPABILITY_COLOR_MOUSE_POINTER) + || pxm->colors_nr <= 2) { + if (state->driver->capabilities & GFX_CAPABILITY_POINTER_PIXMAP_REGISTRY) { + if ((pxm) && (retval = state->driver->register_pixmap(state->driver, pxm))) { + GFXERROR("Pixmap-registering a new mouse pointer failed!\n"); + return retval; + } + } + draw_new = 0; + state->driver->set_pointer(state->driver, pxm); + state->mouse_pointer_in_hw = 1; + } else { + draw_new = 1; + state->mouse_pointer_in_hw = 0; + } + + } else draw_new = 1; + + if (!state->mouse_pointer_in_hw) + draw_old = state->mouse_pointer != NULL; + + + if (draw_old) { + _gfxop_get_pointer_bounds(state, &old_pointer_bounds); + REMOVE_POINTER; + } + + + if (draw_new) { + state->mouse_pointer = pxm; + DRAW_POINTER; + _gfxop_get_pointer_bounds(state, &pointer_bounds); + } + + if (draw_new && state->mouse_pointer) + _gfxop_buffer_propagate_box(state, pointer_bounds, GFX_BUFFER_FRONT); + + if (draw_old) + _gfxop_buffer_propagate_box(state, old_pointer_bounds, GFX_BUFFER_FRONT); + + if (state->mouse_pointer == NULL) + state->mouse_pointer_visible = 0; + else if (!state->mouse_pointer_visible) + state->mouse_pointer_visible = 1; + /* else don't touch it, as it might be VISIBLE_BUT_CLIPPED! */ + + return GFX_OK; +} + + +int +gfxop_set_pointer_cursor(gfx_state_t *state, int nr) +{ + gfx_pixmap_t *new_pointer = NULL; + + BASIC_CHECKS(GFX_FATAL); + + if (nr == GFXOP_NO_POINTER) + new_pointer = NULL; + else { + new_pointer = gfxr_get_cursor(state->resstate, nr); + + if (!new_pointer) { + GFXWARN("Attempt to set invalid pointer #%d\n", nr); + } + } + + return _gfxop_set_pointer(state, new_pointer); +} + + +int +gfxop_set_pointer_view(gfx_state_t *state, int nr, int loop, int cel, point_t *hotspot) +{ + int real_loop = loop; + int real_cel = cel; + gfx_pixmap_t *new_pointer = NULL; + + BASIC_CHECKS(GFX_FATAL); + + new_pointer = _gfxr_get_cel(state, nr, &real_loop, &real_cel, + 0); /* FIXME: For now, don't palettize pointers */ + + if (hotspot) + { + new_pointer->xoffset = hotspot->x; + new_pointer->yoffset = hotspot->y; + } + + if (!new_pointer) { + GFXWARN("Attempt to set invalid pointer #%d\n", nr); + return GFX_ERROR; + } else + { + if (real_loop != loop || real_cel != cel) { + GFXDEBUG("Changed loop/cel from %d/%d to %d/%d in view %d\n", + loop, cel, real_loop, real_cel, nr); + } + return _gfxop_set_pointer(state, new_pointer); + } +} + +int +gfxop_set_pointer_position(gfx_state_t *state, point_t pos) +{ + BASIC_CHECKS(GFX_ERROR); + + state->pointer_pos = pos; + + if (pos.x > 320 || pos.y > 200) + { + GFXWARN("Attempt to place pointer at invalid coordinates (%d, %d)\n", pos.x, pos.y); + return 0; /* Not fatal */ + } + + state->driver->pointer_x = pos.x * state->driver->mode->xfact; + state->driver->pointer_y = pos.y * state->driver->mode->yfact; + + GFXOP_FULL_POINTER_REFRESH; + return 0; +} + +#define SCANCODE_ROWS_NR 3 + +struct scancode_row { + int offset; + const char *keys; +} scancode_rows[SCANCODE_ROWS_NR] = { + {0x10, "QWERTYUIOP[]"}, + {0x1e, "ASDFGHJKL;'\\"}, + {0x2c, "ZXCVBNM,./"} +}; + +static int +_gfxop_scancode(int ch) + /* Calculates a PC keyboard scancode from a character */ +{ + int row; + int c = toupper((char)ch); + + for (row = 0; row < SCANCODE_ROWS_NR; row++) { + const char *keys = scancode_rows[row].keys; + int offset = scancode_rows[row].offset; + + while (*keys) { + if (*keys == c) + return offset << 8; + + offset++; + keys++; + } + } + + return ch; /* not found */ +} + +/* static */ int +_gfxop_shiftify(int c) +{ + char shifted_numbers[] = ")!@#$%^&*("; + + if (c < 256) + { + c = toupper((char)c); + + if (c >= 'A' && c <= 'Z') + return c; + + if (c >= '0' && c <= '9') + return shifted_numbers[c-'0']; + + switch (c) { + case SCI_K_TAB: return SCI_K_SHIFT_TAB; + case ']': return '}'; + case '[': return '{'; + case '`': return '~'; + case '-': return '_'; + case '=': return '+'; + case ';': return ':'; + case '\'': return '"'; + case '\\': return '|'; + case ',': return '<'; + case '.': return '>'; + case '/': return '?'; + default: return c; /* No match */ + } + } + + switch (c) + { + case SCI_K_F1 : return SCI_K_SHIFT_F1; + case SCI_K_F2 : return SCI_K_SHIFT_F2; + case SCI_K_F3 : return SCI_K_SHIFT_F3; + case SCI_K_F4 : return SCI_K_SHIFT_F4; + case SCI_K_F5 : return SCI_K_SHIFT_F5; + case SCI_K_F6 : return SCI_K_SHIFT_F6; + case SCI_K_F7 : return SCI_K_SHIFT_F7; + case SCI_K_F8 : return SCI_K_SHIFT_F8; + case SCI_K_F9 : return SCI_K_SHIFT_F9; + case SCI_K_F10 : return SCI_K_SHIFT_F10; + } + + return c; +} + +static int +_gfxop_numlockify(int c) +{ + switch (c) { + case SCI_K_DELETE: return '.'; + case SCI_K_INSERT: return '0'; + case SCI_K_END: return '1'; + case SCI_K_DOWN: return '2'; + case SCI_K_PGDOWN: return '3'; + case SCI_K_LEFT: return '4'; + case SCI_K_CENTER: return '5'; + case SCI_K_RIGHT: return '6'; + case SCI_K_HOME: return '7'; + case SCI_K_UP: return '8'; + case SCI_K_PGUP: return '9'; + default: return c; /* Unchanged */ + } +} + + +sci_event_t +gfxop_get_event(gfx_state_t *state, unsigned int mask) +{ + sci_event_t error_event = { SCI_EVT_ERROR, 0, 0 }; + sci_event_t event; + gfx_input_event_t **seekerp = &(state->events); + + BASIC_CHECKS(error_event); + if (_gfxop_remove_pointer(state)) { + GFXERROR("Failed to remove pointer before processing event!\n"); + } + + while (*seekerp && !((*seekerp)->event.type & mask)) + seekerp = &((*seekerp)->next); + + if (*seekerp) { + gfx_input_event_t *goner = *seekerp; + event = goner->event; + *seekerp = goner->next; + free(goner); + } else { + event.type = 0; + + if (!(mask & SCI_EVT_NONBLOCK)) + { + do { + if (event.type) { + *seekerp = (gfx_input_event_t*)sci_malloc(sizeof(gfx_input_event_t)); + (*seekerp)->next = NULL; + + event.data = (char) (event.data); + /* Clip illegal bits */ + + (*seekerp)->event = event; + seekerp = &((*seekerp)->next); + } + event = state->driver->get_event(state->driver); + + } while (event.type && !(event.type & mask)); + } + } + + if (_gfxop_full_pointer_refresh(state)) { + GFXERROR("Failed to update the mouse pointer!\n"); + return error_event; + } + + if (event.type == SCI_EVT_KEYBOARD + && !(state->driver->capabilities & GFX_CAPABILITY_KEYTRANSLATE)) { + /* Do we still have to translate the key? */ + + event.character = event.data; + + /* Scancodify if appropriate */ + if (event.buckybits & SCI_EVM_ALT) + event.character = _gfxop_scancode(event.character); + + /* Shift if appropriate */ + else if (((event.buckybits & (SCI_EVM_RSHIFT | SCI_EVM_LSHIFT)) + && !(event.buckybits & SCI_EVM_CAPSLOCK)) + || + (!(event.buckybits & (SCI_EVM_RSHIFT | SCI_EVM_LSHIFT)) + && (event.buckybits & SCI_EVM_CAPSLOCK))) + event.character = _gfxop_shiftify(event.character); + + /* Numlockify if appropriate */ + else if (event.buckybits & SCI_EVM_NUMLOCK) + event.data = _gfxop_numlockify(event.data); + } + + return event; +} + + +/*******************/ +/* View operations */ +/*******************/ + +int +gfxop_lookup_view_get_loops(gfx_state_t *state, int nr) +{ + int loop = 0, cel = 0; + gfxr_view_t *view = NULL; + + BASIC_CHECKS(GFX_ERROR); + + view = gfxr_get_view(state->resstate, nr, &loop, &cel, 0); + + if (!view) { + GFXWARN("Attempt to retreive number of loops from invalid view %d\n", nr); + return 0; + } + + return view->loops_nr; +} + + +int +gfxop_lookup_view_get_cels(gfx_state_t *state, int nr, int loop) +{ + int real_loop = loop, cel = 0; + gfxr_view_t *view = NULL; + + BASIC_CHECKS(GFX_ERROR); + + view = gfxr_get_view(state->resstate, nr, &real_loop, &cel, 0); + + if (!view) { + GFXWARN("Attempt to retreive number of cels from invalid/broken view %d\n", nr); + return 0; + } else if (real_loop != loop) { + GFXWARN("Loop number was corrected from %d to %d in view %d\n", loop, real_loop, nr); + } + + return view->loops[real_loop].cels_nr; +} + + +int +gfxop_check_cel(gfx_state_t *state, int nr, int *loop, int *cel) +{ + BASIC_CHECKS(GFX_ERROR); + + if (!gfxr_get_view(state->resstate, nr, loop, cel, 0)) { + GFXWARN("Attempt to verify loop/cel values for invalid view %d\n", nr); + return GFX_ERROR; + } + + return GFX_OK; +} + +int +gfxop_overflow_cel(gfx_state_t *state, int nr, int *loop, int *cel) +{ + int loop_v = *loop; + int cel_v = *cel; + BASIC_CHECKS(GFX_ERROR); + + if (!gfxr_get_view(state->resstate, nr, &loop_v, &cel_v, 0)) { + GFXWARN("Attempt to verify loop/cel values for invalid view %d\n", nr); + return GFX_ERROR; + } + + if (loop_v != *loop) + *loop = 0; + + if (loop_v != *loop + || cel_v != *cel) + *cel = 0; + + return GFX_OK; +} + + +int +gfxop_get_cel_parameters(gfx_state_t *state, int nr, int loop, int cel, + int *width, int *height, point_t *offset) +{ + gfxr_view_t *view = NULL; + gfx_pixmap_t *pxm = NULL; + BASIC_CHECKS(GFX_ERROR); + + if (!(view = gfxr_get_view(state->resstate, nr, &loop, &cel, 0))) { + GFXWARN("Attempt to get cel parameters for invalid view %d\n", nr); + return GFX_ERROR; + } + + pxm = view->loops[loop].cels[cel]; + *width = pxm->index_xl; + *height = pxm->index_yl; + offset->x = pxm->xoffset; + offset->y = pxm->yoffset; + + return GFX_OK; +} + + +static int +_gfxop_draw_cel_buffer(gfx_state_t *state, int nr, int loop, int cel, + point_t pos, gfx_color_t color, int static_buf, + int palette) +{ + int priority = (color.mask & GFX_MASK_PRIORITY)? color.priority : -1; + int control = (color.mask & GFX_MASK_CONTROL)? color.control : -1; + gfxr_view_t *view = NULL; + gfx_pixmap_t *pxm = NULL; + int old_x, old_y; + BASIC_CHECKS(GFX_FATAL); + + if (!(view = gfxr_get_view(state->resstate, nr, &loop, &cel, palette))) { + GFXWARN("Attempt to draw loop/cel %d/%d in invalid view %d\n", loop, cel, nr); + return GFX_ERROR; + } + pxm = view->loops[loop].cels[cel]; + + old_x = pos.x -= pxm->xoffset; + old_y = pos.y -= pxm->yoffset; + + pos.x *= state->driver->mode->xfact; + pos.y *= state->driver->mode->yfact; + + if (!static_buf) + _gfxop_add_dirty(state, gfx_rect(old_x, old_y, pxm->index_xl, pxm->index_yl)); + + return _gfxop_draw_pixmap(state->driver, pxm, priority, control, + gfx_rect(0, 0, pxm->xl, pxm->yl), + gfx_rect(pos.x, pos.y, pxm->xl, pxm->yl), + state->clip_zone, + static_buf , state->control_map, + static_buf + ? state->static_priority_map + : state->priority_map); +} + + +int +gfxop_draw_cel(gfx_state_t *state, int nr, int loop, int cel, point_t pos, + gfx_color_t color, int palette) +{ + return _gfxop_draw_cel_buffer(state, nr, loop, cel, pos, color, 0, palette); +} + + +int +gfxop_draw_cel_static(gfx_state_t *state, int nr, int loop, int cel, point_t pos, + gfx_color_t color, int palette) +{ + int retval; + rect_t oldclip = state->clip_zone; + + state->clip_zone = gfx_rect_fullscreen; + _gfxop_scale_rect(&(state->clip_zone), state->driver->mode); + retval = gfxop_draw_cel_static_clipped(state, nr, loop, cel, pos, color, + palette); + /* Except that the area it's clipped against is... unusual ;-) */ + state->clip_zone = oldclip; + + return retval; +} + + +int +gfxop_draw_cel_static_clipped(gfx_state_t *state, int nr, int loop, int cel, + point_t pos, gfx_color_t color, int palette) +{ + return _gfxop_draw_cel_buffer(state, nr, loop, cel, pos, color, 1, palette); +} + + +/******************/ +/* Pic operations */ +/******************/ + +static int +_gfxop_set_pic(gfx_state_t *state) +{ + gfx_copy_pixmap_box_i(state->control_map, state->pic->control_map, gfx_rect(0, 0, 320, 200)); + gfx_copy_pixmap_box_i(state->priority_map, state->pic_unscaled->priority_map, gfx_rect(0, 0, 320, 200)); + gfx_copy_pixmap_box_i(state->static_priority_map, state->pic_unscaled->priority_map, gfx_rect(0, 0, 320, 200)); + + _gfxop_install_pixmap(state->driver, state->pic->visual_map); + + if (state->options->pic0_unscaled) + state->pic->priority_map = gfx_pixmap_scale_index_data(state->pic->priority_map, state->driver->mode); + return state->driver->set_static_buffer(state->driver, state->pic->visual_map, state->pic->priority_map); +} + + +void * +gfxop_get_pic_metainfo(gfx_state_t *state) +{ + return (state->pic)? state->pic->internal : NULL; +} + + +int +gfxop_new_pic(gfx_state_t *state, int nr, int flags, int default_palette) +{ + BASIC_CHECKS(GFX_FATAL); + + gfxr_tag_resources(state->resstate); + state->tag_mode = 1; + state->palette_nr = default_palette; + + state->pic = gfxr_get_pic(state->resstate, nr, GFX_MASK_VISUAL, flags, default_palette, 1); + + if (state->driver->mode->xfact == 1 && state->driver->mode->yfact == 1) + state->pic_unscaled = state->pic; + else + state->pic_unscaled = gfxr_get_pic(state->resstate, nr, GFX_MASK_VISUAL, flags, default_palette, 0); + + if (!state->pic || !state->pic_unscaled) { + GFXERROR("Could not retreive background pic %d!\n", nr); + if (state->pic) { + GFXERROR(" -- Inconsistency: scaled pic _was_ retreived!\n"); + } + + if (state->pic_unscaled) { + GFXERROR(" -- Inconsistency: unscaled pic _was_ retreived!\n"); + } + + state->pic = state->pic_unscaled = NULL; + return GFX_ERROR; + } + + state->pic_nr = nr; + + return _gfxop_set_pic(state); +} + + +int +gfxop_add_to_pic(gfx_state_t *state, int nr, int flags, int default_palette) +{ + BASIC_CHECKS(GFX_FATAL); + + if (!state->pic) { + GFXERROR("Attempt to add to pic with no pic active!\n"); + return GFX_ERROR; + } + + if (!(state->pic = gfxr_add_to_pic(state->resstate, state->pic_nr, nr, + GFX_MASK_VISUAL, flags, state->palette_nr, default_palette, 1))) { + GFXERROR("Could not add pic #%d to pic #%d!\n", state->pic_nr, nr); + return GFX_ERROR; + } + state->pic_unscaled = gfxr_add_to_pic(state->resstate, state->pic_nr, nr, + GFX_MASK_VISUAL, flags, + state->palette_nr, + default_palette, 1); + + return _gfxop_set_pic(state); +} + + +/*******************/ +/* Text operations */ +/*******************/ + + +int +gfxop_get_font_height(gfx_state_t *state, int font_nr) +{ + gfx_bitmap_font_t *font; + BASIC_CHECKS(GFX_FATAL); + + font = gfxr_get_font(state->resstate, font_nr, 0); + if (!font) + return GFX_ERROR; + + return font->line_height; +} + +int +gfxop_get_text_params(gfx_state_t *state, int font_nr, const char *text, + int maxwidth, int *width, int *height, int text_flags, + int *lines_nr, int *lineheight, int *lastline_width) +{ + text_fragment_t *textsplits; + gfx_bitmap_font_t *font; + + BASIC_CHECKS(GFX_FATAL); + + font = gfxr_get_font(state->resstate, font_nr, 0); + + if (!font) { + GFXERROR("Attempt to calculate text size with invalid font #%d\n", font_nr); + *width = *height = 0; + return GFX_ERROR; + } + + textsplits = gfxr_font_calculate_size(font, maxwidth, text, width, + height, lines_nr, + lineheight, lastline_width, + (state->options->workarounds & GFX_WORKAROUND_WHITESPACE_COUNT) + | text_flags); + + + if (!textsplits) { + GFXERROR("Could not calculate text size!"); + *width = *height = 0; + return GFX_ERROR; + } + + free(textsplits); + return GFX_OK; +} + + +#define COL_XLATE(des,src) \ + des = src.visual; /* The new gfx_color_t structure makes things a lot easier :-) */ /* \ + if (gfxop_set_color(state, &src, \ + src.visual.r, \ + src.visual.g, \ + src.visual.b, \ + src.alpha, \ + src.priority, \ + src.control)) \ + { \ + GFXERROR("Unable to set up colors"); \ + return NULL; \ + } +*/ + +gfx_text_handle_t * +gfxop_new_text(gfx_state_t *state, int font_nr, char *text, int maxwidth, + gfx_alignment_t halign, gfx_alignment_t valign, + gfx_color_t color1, gfx_color_t color2, gfx_color_t bg_color, + int flags) +{ + gfx_text_handle_t *handle = {0}; + gfx_bitmap_font_t *font = {0}; + int i; + gfx_pixmap_color_t pxm_col1, pxm_col2, pxm_colbg= {0}; + BASIC_CHECKS(NULL); + + COL_XLATE(pxm_col1, color1); + COL_XLATE(pxm_col2, color2); + COL_XLATE(pxm_colbg, bg_color); + + font = gfxr_get_font(state->resstate, font_nr, 0); + + if (!font) { + GFXERROR("Attempt to draw text with invalid font #%d\n", font_nr); + return NULL; + } + + handle = (gfx_text_handle_t*)sci_malloc(sizeof(gfx_text_handle_t)); + + handle->text = (char*)sci_malloc(strlen(text) + 1); + strcpy(handle->text, text); + handle->halign = halign; + handle->valign = valign; + handle->line_height = font->line_height; + + handle->lines = + gfxr_font_calculate_size(font, maxwidth, handle->text, &(handle->width), &(handle->height), + &(handle->lines_nr), + NULL, NULL, + ((state->options->workarounds & GFX_WORKAROUND_WHITESPACE_COUNT)? + GFXR_FONT_FLAG_COUNT_WHITESPACE : 0) + | flags); + + if (!handle->lines) { + free(handle->text); + free(handle); + GFXERROR("Could not calculate text parameters in font #%d\n", font_nr); + return NULL; + } + + + if (flags & GFXR_FONT_FLAG_NO_NEWLINES) { + handle->lines_nr = 1; + handle->lines->length = strlen(text); + } + + handle->text_pixmaps = (gfx_pixmap_t**)sci_malloc(sizeof(gfx_pixmap_t *) * handle->lines_nr); + + for (i = 0; i < handle->lines_nr; i++) { + int chars_nr = handle->lines[i].length; + + handle->text_pixmaps[i] = gfxr_draw_font(font, handle->lines[i].offset, chars_nr, + (color1.mask & GFX_MASK_VISUAL)? &pxm_col1 : NULL, + (color2.mask & GFX_MASK_VISUAL)? &pxm_col2 : NULL, + (bg_color.mask & GFX_MASK_VISUAL)? &pxm_colbg : NULL); + + if (!handle->text_pixmaps[i]) { + int j; + + for (j = 0; j < i; j++) + gfx_free_pixmap(state->driver, handle->text_pixmaps[j]); + sci_free(handle->text_pixmaps); + sci_free(handle->text); + sci_free(handle->lines); + GFXERROR("Failed to draw text pixmap for line %d/%d\n", i, handle->lines_nr); + sci_free(handle); + return NULL; + } + } + + handle->font = font; + + handle->priority = (color1.mask & GFX_MASK_PRIORITY)? color1.priority : -1; + handle->control = (color1.mask & GFX_MASK_CONTROL)? color1.control : -1; + + return handle; +} + + +int +gfxop_free_text(gfx_state_t *state, gfx_text_handle_t *handle) +{ + int j; + + BASIC_CHECKS(GFX_ERROR); + + if (handle->text_pixmaps) { + for (j = 0; j < handle->lines_nr; j++) + gfx_free_pixmap(state->driver, handle->text_pixmaps[j]); + free(handle->text_pixmaps); + } + + free(handle->text); + free(handle->lines); + free(handle); + return GFX_OK; +} + + +int +gfxop_draw_text(gfx_state_t *state, gfx_text_handle_t *handle, rect_t zone) +{ + int line_height; + rect_t pos; + int i; + BASIC_CHECKS(GFX_FATAL); + REMOVE_POINTER; + + if (!handle) { + GFXERROR("Attempt to draw text with NULL handle!\n"); + return GFX_ERROR; + } + + if (!handle->lines_nr) { + GFXDEBUG("Skipping draw_text operation because number of lines is zero\n"); + return GFX_OK; + } + + _gfxop_scale_rect(&zone, state->driver->mode); + + line_height = handle->line_height * state->driver->mode->yfact; + + pos.y = zone.y; + + switch (handle->valign) { + + case ALIGN_TOP: + break; + + case ALIGN_CENTER: + pos.y += (zone.yl - (line_height * handle->lines_nr)) >> 1; + break; + + case ALIGN_BOTTOM: + pos.y += (zone.yl - (line_height * handle->lines_nr)); + break; + + default: + GFXERROR("Invalid vertical alignment %d!\n", handle->valign); + return GFX_FATAL; /* Internal error... */ + } + + for (i = 0; i < handle->lines_nr; i++) { + + gfx_pixmap_t *pxm = handle->text_pixmaps[i]; + + if (!pxm->data) { + gfx_xlate_pixmap(pxm, state->driver->mode, state->options->text_xlate_filter); + gfxr_endianness_adjust(pxm, state->driver->mode); /* FIXME: resmgr layer! */ + } + if (!pxm) { + GFXERROR("Could not find text pixmap %d/%d\n", i, handle->lines_nr); + return GFX_ERROR; + } + + pos.x = zone.x; + + switch (handle->halign) { + + case ALIGN_LEFT: + break; + + case ALIGN_CENTER: + pos.x += (zone.xl - pxm->xl) >> 1; + break; + + case ALIGN_RIGHT: + pos.x += (zone.xl - pxm->xl); + break; + + default: + GFXERROR("Invalid vertical alignment %d!\n", handle->valign); + return GFX_FATAL; /* Internal error... */ + } + + pos.xl = pxm->xl; + pos.yl = pxm->yl; + + _gfxop_add_dirty(state, pos); + + _gfxop_draw_pixmap(state->driver, pxm, handle->priority, handle->control, + gfx_rect(0, 0, pxm->xl, pxm->yl), pos, state->clip_zone, 0, + state->control_map, state->priority_map); + + pos.y += line_height; + } + + return GFX_OK; +} + + +gfx_pixmap_t * +gfxop_grab_pixmap(gfx_state_t *state, rect_t area) +{ + gfx_pixmap_t *pixmap = NULL; + rect_t resultzone; /* Ignored for this application */ + BASIC_CHECKS(NULL); + if (_gfxop_remove_pointer(state)) { + GFXERROR("Could not remove pointer!\n"); + return NULL; + } + + _gfxop_scale_rect(&area, state->driver->mode); + if (_gfxop_grab_pixmap(state, &pixmap, area.x, area.y, area.xl, area.yl, 0, &resultzone)) + return NULL; /* area CUT the visual screen had a null or negative size */ + + pixmap->flags |= GFX_PIXMAP_FLAG_PALETTE_SET | GFX_PIXMAP_FLAG_DONT_UNALLOCATE_PALETTE; + + return pixmap; +} + +int +gfxop_draw_pixmap(gfx_state_t *state, gfx_pixmap_t *pxm, rect_t zone, point_t pos) +{ + rect_t target; + BASIC_CHECKS(GFX_ERROR); + + if (!pxm) { + GFXERROR("Attempt to draw NULL pixmap!\n"); + return GFX_ERROR; + } + + REMOVE_POINTER; + + target = gfx_rect(pos.x, pos.y, zone.xl, zone.yl); + + _gfxop_add_dirty(state, target); + + if (!pxm) { + GFXERROR("Attempt to draw_pixmap with pxm=NULL\n"); + return GFX_ERROR; + } + + _gfxop_scale_rect(&zone, state->driver->mode); + _gfxop_scale_rect(&target, state->driver->mode); + + return _gfxop_draw_pixmap(state->driver, pxm, -1, -1, zone, target, + gfx_rect(0, 0, 320*state->driver->mode->xfact, + 200*state->driver->mode->yfact), 0, NULL, NULL); +} + +int +gfxop_free_pixmap(gfx_state_t *state, gfx_pixmap_t *pxm) +{ + BASIC_CHECKS(GFX_ERROR); + gfx_free_pixmap(state->driver, pxm); + return GFX_OK; +} + diff --git a/engines/sci/gfx/resmgr.c b/engines/sci/gfx/resmgr.c new file mode 100644 index 0000000000..2a525f675f --- /dev/null +++ b/engines/sci/gfx/resmgr.c @@ -0,0 +1,708 @@ +/*************************************************************************** + resmgr.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Resource manager core part */ + + +#include +#include +#include +#include +#include + +#undef TIME_PICDRAWING + +/* Invalid hash mode: Used to invalidate modified pics */ +#define MODE_INVALID -1 + +struct param_struct { + int args[4]; + gfx_driver_t *driver; +}; + + +gfx_resstate_t * +gfxr_new_resource_manager(int version, gfx_options_t *options, + gfx_driver_t *driver, void *misc_payload) +{ + gfx_resstate_t *state = (gfx_resstate_t*)sci_malloc(sizeof(gfx_resstate_t)); + int ii; + + state->version = version; + state->options = options; + state->driver = driver; + state->misc_payload = misc_payload; + + state->tag_lock_counter = state->lock_counter = 0; + for (ii = 0; ii < GFX_RESOURCE_TYPES_NR; ii++) { + gfx_resource_type_t i = (gfx_resource_type_t) ii; + sbtree_t *tree; + int entries_nr; + int *resources = gfxr_interpreter_get_resources(state, i, version, + &entries_nr, misc_payload); + + if (!resources) + state->resource_trees[i] = NULL; + else { + tree = sbtree_new(entries_nr, resources); + if (!tree) { + GFXWARN("Failed to allocate tree for %d entries of resource type %d!\n", entries_nr, i); + } + state->resource_trees[i] = tree; + free(resources); + } + } + return state; +} + +#define FREEALL(freecmd, type) \ + if (resource->scaled_data.type) \ + freecmd(driver, resource->scaled_data.type); \ + resource->scaled_data.type = NULL; \ + if (resource->unscaled_data.type) \ + freecmd(driver, resource->unscaled_data.type); \ + resource->unscaled_data.type = NULL; + +#define FREEALL_SIMPLE(freecmd, type) \ + if (resource->scaled_data.type) \ + freecmd(resource->scaled_data.type); \ + resource->scaled_data.type = NULL; \ + if (resource->unscaled_data.type) \ + freecmd(resource->unscaled_data.type); \ + resource->unscaled_data.type = NULL; + + +void +gfxr_free_resource(gfx_driver_t *driver, gfx_resource_t *resource, int type) +{ + if (!resource) + return; + + switch (type) { + + case GFX_RESOURCE_TYPE_VIEW: + FREEALL(gfxr_free_view, view); + break; + + case GFX_RESOURCE_TYPE_PIC: + FREEALL(gfxr_free_pic, pic); + break; + + case GFX_RESOURCE_TYPE_FONT: + FREEALL_SIMPLE(gfxr_free_font, font); + break; + + case GFX_RESOURCE_TYPE_CURSOR: + FREEALL(gfx_free_pixmap, pointer); + break; + + default: + GFXWARN("Attempt to free invalid resource type %d\n", type); + } + + free(resource); +} + +#undef FREECMD + + +void * +gfxr_sbtree_free_func(sbtree_t *tree, const int key, const void *value, void *args) +{ + struct param_struct *params = (struct param_struct *) args; + int type = params->args[0]; + gfx_driver_t *driver = params->driver; + + if (value) + gfxr_free_resource(driver, (gfx_resource_t *) value, type); + + return NULL; +} + +#define SBTREE_FREE_TAGGED_ARG_TYPE 0 +#define SBTREE_FREE_TAGGED_ARG_TAGVALUE 1 +#define SBTREE_FREE_TAGGED_ARG_ACTION 2 + +#define ARG_ACTION_RESET 0 +#define ARG_ACTION_DECREMENT 1 + +void * +gfxr_sbtree_free_tagged_func(sbtree_t *tree, const int key, const void *value, void *args) +{ + struct param_struct *params = (struct param_struct *) args; + int type = params->args[SBTREE_FREE_TAGGED_ARG_TYPE]; + int tag_value = params->args[SBTREE_FREE_TAGGED_ARG_TAGVALUE]; + int action = params->args[SBTREE_FREE_TAGGED_ARG_ACTION]; + gfx_driver_t *driver = params->driver; + gfx_resource_t *resource = (gfx_resource_t *) value; + if (resource) { + if (resource->lock_sequence_nr < tag_value) { + gfxr_free_resource(driver, (gfx_resource_t *) value, type); + return NULL; + } else { + if (action == ARG_ACTION_RESET) + resource->lock_sequence_nr = 0; + else + (resource->lock_sequence_nr)--; + return (void *) value; + } + } else return NULL; +} + + +void +gfxr_free_all_resources(gfx_driver_t *driver, gfx_resstate_t *state) +{ + struct param_struct params; + int i; + sbtree_t *tree = NULL; + + for (i = 0; i < GFX_RESOURCE_TYPES_NR; i++) + if ((tree = state->resource_trees[i])) { + params.args[0] = i; + params.driver = driver; + sbtree_foreach(tree, (void *) ¶ms, gfxr_sbtree_free_func); + } +} + +void +gfxr_free_resource_manager(gfx_driver_t *driver, gfx_resstate_t *state) +{ + struct param_struct params; + int i; + sbtree_t *tree = NULL; + + for (i = 0; i < GFX_RESOURCE_TYPES_NR; i++) + if ((tree = state->resource_trees[i])) { + params.args[0] = i; + params.driver = driver; + sbtree_foreach(tree, (void *) ¶ms, gfxr_sbtree_free_func); + sbtree_free(tree); + } + + free(state); +} + + +void +gfxr_tag_resources(gfx_resstate_t *state) +{ + (state->tag_lock_counter)++; +} + + + +void +gfxr_free_tagged_resources(gfx_driver_t *driver, gfx_resstate_t *state) +{ + /* Current heuristics: free tagged views and old pics */ + struct param_struct params; + + if (state->resource_trees[GFX_RESOURCE_TYPE_VIEW]) { + params.args[SBTREE_FREE_TAGGED_ARG_TYPE] = GFX_RESOURCE_TYPE_VIEW; + params.args[SBTREE_FREE_TAGGED_ARG_TAGVALUE] = state->tag_lock_counter; + params.args[SBTREE_FREE_TAGGED_ARG_ACTION] = ARG_ACTION_RESET; + params.driver = driver; + sbtree_foreach(state->resource_trees[GFX_RESOURCE_TYPE_VIEW], (void *) ¶ms, gfxr_sbtree_free_tagged_func); + } + + if (state->resource_trees[GFX_RESOURCE_TYPE_PIC]) { + params.args[SBTREE_FREE_TAGGED_ARG_TYPE] = GFX_RESOURCE_TYPE_PIC; + params.args[SBTREE_FREE_TAGGED_ARG_TAGVALUE] = 0; + params.args[SBTREE_FREE_TAGGED_ARG_ACTION] = ARG_ACTION_DECREMENT; + params.driver = driver; + sbtree_foreach(state->resource_trees[GFX_RESOURCE_TYPE_PIC], (void *) ¶ms, gfxr_sbtree_free_tagged_func); + } + + state->tag_lock_counter = 0; +} + + +#define XLATE_AS_APPROPRIATE(key, entry) \ + if (maps & key) { \ + if (res->unscaled_data.pic \ + && (force || !res->unscaled_data.pic->entry->data)) { \ + if (key == GFX_MASK_VISUAL) \ + gfx_get_res_config(options, res->unscaled_data.pic->entry); \ + gfx_xlate_pixmap(res->unscaled_data.pic->entry, mode, filter); \ + } if (scaled && res->scaled_data.pic \ + && (force || !res->scaled_data.pic->entry->data)) { \ + if (key == GFX_MASK_VISUAL) \ + gfx_get_res_config(options, res->scaled_data.pic->entry); \ + gfx_xlate_pixmap(res->scaled_data.pic->entry, mode, filter); \ + } \ + } + +static gfxr_pic_t * +gfxr_pic_xlate_common(gfx_resource_t *res, int maps, int scaled, + int force, gfx_mode_t *mode, gfx_xlate_filter_t filter, int endianize, + gfx_options_t *options) +{ + XLATE_AS_APPROPRIATE(GFX_MASK_VISUAL, visual_map); + XLATE_AS_APPROPRIATE(GFX_MASK_PRIORITY, priority_map); + XLATE_AS_APPROPRIATE(GFX_MASK_CONTROL, control_map); + + if (endianize && (maps & GFX_MASK_VISUAL) && res->scaled_data.pic->visual_map) + gfxr_endianness_adjust(res->scaled_data.pic->visual_map, mode); + + + return scaled? res->scaled_data.pic : res->unscaled_data.pic; +} +#undef XLATE_AS_APPROPRIATE + + +gfxr_pic_t * +gfxr_get_pic(gfx_resstate_t *state, int nr, int maps, int flags, int default_palette, int scaled) +{ + gfxr_pic_t *npic = NULL; + gfx_resource_type_t restype = GFX_RESOURCE_TYPE_PIC; + sbtree_t *tree = state->resource_trees[restype]; + gfx_resource_t *res = NULL; + int hash = gfxr_interpreter_options_hash(restype, state->version, + state->options, state->misc_payload, 0); + int must_post_process_pic = 0; + int need_unscaled = + (state->driver->mode->xfact != 1 || state->driver->mode->yfact != 1); + + if (!tree) + return NULL; + + hash |= (flags << 20) | ((default_palette & 0x7) << 28); + + res = (gfx_resource_t *) sbtree_get(tree, nr); + + if (!res || res->mode != hash) { + gfxr_pic_t *pic; + gfxr_pic_t *unscaled_pic = NULL; + + if (state->options->pic0_unscaled) { + need_unscaled = 0; + pic = gfxr_interpreter_init_pic(state->version, + &mode_1x1_color_index, + GFXR_RES_ID(restype, nr), + state->misc_payload); + } else pic = gfxr_interpreter_init_pic(state->version, + state->driver->mode, + GFXR_RES_ID(restype, nr), + state->misc_payload); + + if (!pic) { + GFXERROR("Failed to allocate scaled pic!\n"); + return NULL; + } + + gfxr_interpreter_clear_pic(state->version, pic, state->misc_payload); + + if (need_unscaled) { + unscaled_pic = gfxr_interpreter_init_pic(state->version, + &mode_1x1_color_index, + GFXR_RES_ID(restype, nr), + state->misc_payload); + if (!unscaled_pic) { + GFXERROR("Failed to allocate unscaled pic!\n"); + return NULL; + } + gfxr_interpreter_clear_pic(state->version, unscaled_pic, + state->misc_payload); + } +#ifdef TIME_PICDRAWING + {long start_sec, start_usec; + long end_sec, end_usec; + sci_gettime(&start_sec, &start_usec); +#endif + if (gfxr_interpreter_calculate_pic(state, pic, unscaled_pic, flags, + default_palette, nr, + state->misc_payload)) { + gfxr_free_pic(state->driver, pic); + if (unscaled_pic) + gfxr_free_pic(state->driver, unscaled_pic); + + return NULL; + } +#ifdef TIME_PICDRAWING + sci_gettime(&end_sec, &end_usec); + printf("\nTIME: %d for drawing pic.%03d\n", + (end_sec - start_sec) * 1000000 + (end_usec - start_usec), nr); + } +#endif + + if (!res) { + res = (gfx_resource_t*)sci_malloc(sizeof(gfx_resource_t)); + res->ID = GFXR_RES_ID(restype, nr); + res->lock_sequence_nr = state->options->buffer_pics_nr; + sbtree_set(tree, nr, (void *) res); + } else { + gfxr_free_pic(state->driver, res->scaled_data.pic); + if (res->unscaled_data.pic) + gfxr_free_pic(state->driver, res->unscaled_data.pic); + } + + res->mode = hash; + res->scaled_data.pic = pic; + res->unscaled_data.pic = unscaled_pic; + } else { + res->lock_sequence_nr = state->options->buffer_pics_nr; /* Update lock counter */ + } + + must_post_process_pic = res->scaled_data.pic->visual_map->data == NULL; + /* If the pic was only just drawn, we'll have to antialiase and endianness-adjust it now */ + + npic = gfxr_pic_xlate_common(res, maps, + scaled || state->options->pic0_unscaled, + 0, state->driver->mode, + state->options->pic_xlate_filter, 0, + state->options); + + + if (must_post_process_pic) { + + if (scaled || state->options->pic0_unscaled && maps & GFX_MASK_VISUAL) + gfxr_antialiase(npic->visual_map, state->driver->mode, + state->options->pic0_antialiasing); + + gfxr_endianness_adjust(npic->visual_map, state->driver->mode); + } + + return npic; +} + + +static void +set_pic_id(gfx_resource_t *res, int id) +{ + if (res->scaled_data.pic) { + gfxr_pic_t *pic = res->scaled_data.pic; + pic->control_map->ID = id; + pic->priority_map->ID = id; + pic->visual_map->ID = id; + } + + if (res->unscaled_data.pic) { + gfxr_pic_t *pic = res->unscaled_data.pic; + pic->control_map->ID = id; + pic->priority_map->ID = id; + pic->visual_map->ID = id; + } +} + +static int +get_pic_id(gfx_resource_t *res) +{ + if (res->scaled_data.pic) + return res->scaled_data.pic->visual_map->ID; + else + return res->unscaled_data.pic->visual_map->ID; +} + +static void +_gfxr_unscale_pixmap_index_data(gfx_pixmap_t *pxm, gfx_mode_t *mode) +{ + int xmod = mode->xfact; /* Step size horizontally */ + int ymod = pxm->index_xl * mode->yfact; /* Vertical step size */ + int maxpos = pxm->index_xl * pxm->index_yl; + int pos; + byte *dest = pxm->index_data; + + if (!(pxm->flags & GFX_PIXMAP_FLAG_SCALED_INDEX)) + return; /* It's not scaled! */ + + for (pos = 0; pos < maxpos; pos += ymod) { + int c; + + for (c = 0; c < pxm->index_xl; c += xmod) + *dest++ = pxm->index_data[pos + c]; /* No overwrite since + ** line and offset + ** readers move much + ** faster + ** (proof by in- + ** duction, trivial + ** and left to the + ** reader) */ + } + + pxm->index_xl /= mode->xfact; + pxm->index_yl /= mode->yfact; + pxm->flags &= ~GFX_PIXMAP_FLAG_SCALED_INDEX; +} + + +gfxr_pic_t * +gfxr_add_to_pic(gfx_resstate_t *state, int old_nr, int new_nr, int maps, int flags, + int old_default_palette, int default_palette, int scaled) +{ + gfx_resource_type_t restype = GFX_RESOURCE_TYPE_PIC; + sbtree_t *tree = state->resource_trees[restype]; + gfxr_pic_t *pic = NULL; + gfx_resource_t *res = NULL; + int hash = gfxr_interpreter_options_hash(restype, state->version, + state->options, + state->misc_payload, 0); + int need_unscaled = !(state->options->pic0_unscaled) + && (state->driver->mode->xfact != 1 || state->driver->mode->yfact != 1); + + if (!tree) { + GFXERROR("No pics registered\n"); + return NULL; + } + + res = (gfx_resource_t *) sbtree_get(tree, old_nr); + + if (!res || + (res->mode != MODE_INVALID + && res->mode != hash)) { + gfxr_get_pic(state, old_nr, 0, flags, old_default_palette, scaled); + + res = (gfx_resource_t *) sbtree_get(tree, old_nr); + + if (!res) { + GFXWARN("Attempt to add pic %d to non-existing pic %d\n", new_nr, old_nr); + return NULL; + } + } + + if (state->options->pic0_unscaled) /* Unscale priority map, if we scaled it earlier */ + _gfxr_unscale_pixmap_index_data(res->scaled_data.pic->priority_map, state->driver->mode); + + if (scaled) { + res->lock_sequence_nr = state->options->buffer_pics_nr; + + gfxr_interpreter_calculate_pic(state, res->scaled_data.pic, need_unscaled? res->unscaled_data.pic : NULL, + flags | DRAWPIC01_FLAG_OVERLAID_PIC, + default_palette, new_nr, state->misc_payload); + } + + res->mode = MODE_INVALID; /* Invalidate */ + + if (state->options->pic0_unscaled) /* Scale priority map again, if needed */ + res->scaled_data.pic->priority_map = gfx_pixmap_scale_index_data(res->scaled_data.pic->priority_map, state->driver->mode); + { + int old_ID = get_pic_id(res); + set_pic_id(res, GFXR_RES_ID(restype, new_nr)); /* To ensure that our graphical translation optoins work properly */ + pic = gfxr_pic_xlate_common(res, maps, scaled, 1, state->driver->mode, + state->options->pic_xlate_filter, 1, + state->options); + set_pic_id(res, old_ID); + } + + if (scaled || state->options->pic0_unscaled && maps & GFX_MASK_VISUAL) + gfxr_antialiase(pic->visual_map, state->driver->mode, + state->options->pic0_antialiasing); + + return pic; +} + + +gfxr_view_t * +gfxr_get_view(gfx_resstate_t *state, int nr, int *loop, int *cel, int palette) +{ + gfx_resource_type_t restype = GFX_RESOURCE_TYPE_VIEW; + sbtree_t *tree = state->resource_trees[restype]; + gfx_resource_t *res = NULL; + int hash = gfxr_interpreter_options_hash(restype, state->version, + state->options, state->misc_payload, + palette); + gfxr_view_t *view = NULL; + gfxr_loop_t *loop_data = NULL; + gfx_pixmap_t *cel_data = NULL; + + if (!tree) + return NULL; + + res = (gfx_resource_t *) sbtree_get(tree, nr); + + if (!res || res->mode != hash) { + view = gfxr_interpreter_get_view(state, nr, state->misc_payload, palette); + + if (!view) + return NULL; + + if (!res) { + res = (gfx_resource_t*)sci_malloc(sizeof(gfx_resource_t)); + res->scaled_data.view = NULL; + res->ID = GFXR_RES_ID(restype, nr); + res->lock_sequence_nr = state->tag_lock_counter; + res->mode = hash; + sbtree_set(tree, nr, (void *) res); + } else { + gfxr_free_view(state->driver, res->unscaled_data.view); + } + + res->mode = hash; + res->unscaled_data.view = view; + + } else { + res->lock_sequence_nr = state->tag_lock_counter; /* Update lock counter */ + view = res->unscaled_data.view; + } + + if (*loop < 0) + *loop = 0; + else + if (*loop >= view->loops_nr) + *loop = view->loops_nr - 1; + + if (*loop < 0) { + GFXWARN("View %d has no loops\n", nr); + return NULL; + } + + loop_data = view->loops + (*loop); + if (loop_data == NULL) + { + GFXWARN("Trying to load invalid loop %d of view %d\n", *loop, nr); + return NULL; + } + + if (*cel < 0) + { + sciprintf("Resetting cel! %d\n", *cel); + *cel = 0; + } + else + if (*cel >= loop_data->cels_nr) + *cel = loop_data->cels_nr - 1; + + if (*cel < 0) { + GFXWARN("View %d loop %d has no cels\n", nr, *loop); + return NULL; + } + + cel_data = loop_data->cels[*cel]; + if (loop_data == NULL) + { + GFXWARN("Trying to load invalid view/loop/cel %d/%d/%d\n", nr, *loop, *cel); + return NULL; + } + + if (!cel_data->data) { + gfx_get_res_config(state->options, cel_data); + gfx_xlate_pixmap(cel_data, state->driver->mode, state->options->view_xlate_filter); + gfxr_endianness_adjust(cel_data, state->driver->mode); + } + + return view; +} + +extern gfx_bitmap_font_t gfxfont_5x8; +extern gfx_bitmap_font_t gfxfont_6x10; + +gfx_bitmap_font_t * +gfxr_get_font(gfx_resstate_t *state, int nr, int scaled) +{ + gfx_resource_type_t restype = GFX_RESOURCE_TYPE_FONT; + sbtree_t *tree = NULL; + gfx_resource_t *res = NULL; + int hash; + + if (nr == GFX_FONT_BUILTIN_5x8) + return &gfxfont_5x8; + else if (nr == GFX_FONT_BUILTIN_6x10) + return &gfxfont_6x10; + + tree = state->resource_trees[restype]; + + hash = gfxr_interpreter_options_hash(restype, state->version, + state->options, state->misc_payload, 0); + + if (!tree) + return NULL; + + res = (gfx_resource_t *) sbtree_get(tree, nr); + + if (!res || res->mode != hash) { + gfx_bitmap_font_t *font = gfxr_interpreter_get_font(state, nr, + state->misc_payload); + + if (!font) + return NULL; + + if (!res) { + res = (gfx_resource_t*)sci_malloc(sizeof(gfx_resource_t)); + res->scaled_data.font = NULL; + res->ID = GFXR_RES_ID(restype, nr); + res->lock_sequence_nr = state->tag_lock_counter; + res->mode = hash; + sbtree_set(tree, nr, (void *) res); + } else { + gfxr_free_font(res->unscaled_data.font); + } + + res->unscaled_data.font = font; + + return font; + } else { + res->lock_sequence_nr = state->tag_lock_counter; /* Update lock counter */ + if (res->unscaled_data.pointer) + return res->unscaled_data.font; + else + return res->scaled_data.font; + } +} + + +gfx_pixmap_t * +gfxr_get_cursor(gfx_resstate_t *state, int nr) +{ + gfx_resource_type_t restype = GFX_RESOURCE_TYPE_CURSOR; + sbtree_t *tree = state->resource_trees[restype]; + gfx_resource_t *res = NULL; + int hash = gfxr_interpreter_options_hash(restype, state->version, + state->options, state->misc_payload, 0); + + if (!tree) + return NULL; + + res = (gfx_resource_t *) sbtree_get(tree, nr); + + if (!res || res->mode != hash) { + gfx_pixmap_t *cursor = gfxr_interpreter_get_cursor(state, nr, + state->misc_payload); + + if (!cursor) + return NULL; + + if (!res) { + res = (gfx_resource_t*)sci_malloc(sizeof(gfx_resource_t)); + res->scaled_data.pointer = NULL; + res->ID = GFXR_RES_ID(restype, nr); + res->lock_sequence_nr = state->tag_lock_counter; + res->mode = hash; + sbtree_set(tree, nr, (void *) res); + } else { + gfx_free_pixmap(state->driver, res->unscaled_data.pointer); + } + gfx_get_res_config(state->options, cursor); + gfx_xlate_pixmap(cursor, state->driver->mode, state->options->cursor_xlate_filter); + gfxr_endianness_adjust(cursor, state->driver->mode); + + res->unscaled_data.pointer = cursor; + + return cursor; + } else { + res->lock_sequence_nr = state->tag_lock_counter; /* Update lock counter */ + return res->unscaled_data.pointer; + } +} diff --git a/engines/sci/gfx/resource/Makefile.am b/engines/sci/gfx/resource/Makefile.am new file mode 100644 index 0000000000..4a943e67f0 --- /dev/null +++ b/engines/sci/gfx/resource/Makefile.am @@ -0,0 +1,7 @@ +INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@ +LDADD = $(LDADD_ALL) +EXTRA_DIST = sci_picfill.c sci_picfill_aux.c + +noinst_LIBRARIES = libsciresources.a +libsciresources_a_SOURCES = sci_font.c sci_resmgr.c sci_pic_0.c sci_view_0.c sci_cursor_0.c sci_pal_1.c \ + sci_view_1.c diff --git a/engines/sci/gfx/resource/sci_cursor_0.c b/engines/sci/gfx/resource/sci_cursor_0.c new file mode 100644 index 0000000000..18960516c3 --- /dev/null +++ b/engines/sci/gfx/resource/sci_cursor_0.c @@ -0,0 +1,107 @@ +/*************************************************************************** + cursor_0.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include +#include + + +#define CURSOR_RESOURCE_SIZE 68 +#define CURSOR_SIZE 16 + +#define GFX_SCI01_CURSOR_COLORS_NR 3 +#define GFX_SCI0_CURSOR_COLORS_NR 2 + +#define GFX_CURSOR_TRANSPARENT 255 + +gfx_pixmap_color_t gfx_sci01_cursor_colors[GFX_SCI01_CURSOR_COLORS_NR] = { + {GFX_COLOR_INDEX_UNMAPPED, 0x00, 0x00, 0x00}, + {GFX_COLOR_INDEX_UNMAPPED, 0xff, 0xff, 0xff}, + {GFX_COLOR_INDEX_UNMAPPED, 0xaa, 0xaa, 0xaa}}; + + +static gfx_pixmap_t * +_gfxr_draw_cursor(int id, byte *resource, int size, int sci01) +{ + int colors[4] = {0, 1, GFX_CURSOR_TRANSPARENT, 1}; + int line; + byte *data; + gfx_pixmap_t *retval; + + if (sci01) + colors[3] = 2; + + if (size != CURSOR_RESOURCE_SIZE) { + GFXERROR("Expected resource size of %d, but found %d\n", CURSOR_RESOURCE_SIZE, size); + return NULL; + } + + retval = gfx_pixmap_alloc_index_data(gfx_new_pixmap(CURSOR_SIZE, CURSOR_SIZE, id, 0, 0)); + retval->colors = gfx_sci01_cursor_colors; + retval->colors_nr = sci01? GFX_SCI01_CURSOR_COLORS_NR : GFX_SCI0_CURSOR_COLORS_NR; + retval->flags |= GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + retval->color_key = GFX_CURSOR_TRANSPARENT; + + if (sci01) { + retval->xoffset = get_int_16(resource); + retval->yoffset = get_int_16(resource + 2); + } else if (resource[3]) /* center */ + retval->xoffset = retval->yoffset = CURSOR_SIZE / 2; + else + retval->xoffset = retval->yoffset = 0; + + resource += 4; + + data = retval->index_data; + for (line = 0; line < 16; line++) { + int mask_a = get_int_16(resource + (line << 1)); + int mask_b = get_int_16(resource + 32 + (line << 1)); + int i; + + for (i = 0; i < 16; i++) { + int color_code = ((mask_a << i) & 0x8000) + | (((mask_b << i) >> 1) & 0x4000); + + *data++ = colors[color_code >> 14]; + } + } + return retval; +} + + +gfx_pixmap_t * +gfxr_draw_cursor0(int id, byte *resource, int size) +{ + return _gfxr_draw_cursor(id, resource, size, 0); +} + +gfx_pixmap_t * +gfxr_draw_cursor01(int id, byte *resource, int size) +{ + return _gfxr_draw_cursor(id, resource, size, 1); +} + diff --git a/engines/sci/gfx/resource/sci_font.c b/engines/sci/gfx/resource/sci_font.c new file mode 100644 index 0000000000..5bdb3be1a1 --- /dev/null +++ b/engines/sci/gfx/resource/sci_font.c @@ -0,0 +1,153 @@ +/*************************************************************************** + sci_font.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include +#include +#include + + +extern int font_counter; + + +#define FONT_HEIGHT_OFFSET 4 +#define FONT_MAXCHAR_OFFSET 2 + +static int +calc_char(byte *dest, int total_width, int total_height, byte *src, int size) +{ + int width = src[0]; + int height = src[1]; + int byte_width = (width + 7) >> 3; + int y; + + src += 2; + + if ((width >> 3) > total_width || height > total_height) { + GFXERROR("Weird character: width=%d/%d, height=%d/%d\n", width, total_width, height, total_height); + return GFX_ERROR; + } + + if (byte_width * height + 2 > size) { + GFXERROR("Character extends to %d of %d allowed bytes\n", byte_width * height + 2, size); + return GFX_ERROR; + } + + for (y = 0; y < height; y++) { + memcpy(dest, src, byte_width); + src += byte_width; + dest += total_width; + } + + return GFX_OK; +} + + +gfx_bitmap_font_t * +gfxr_read_font(int id, byte *resource, int size) +{ + gfx_bitmap_font_t *font = (gfx_bitmap_font_t*)sci_calloc(sizeof(gfx_bitmap_font_t), 1); + int chars_nr; + int max_width = 0, max_height; + int i; + + ++font_counter; + + if (size < 6) { + GFXERROR("Font %04x size is %d- this is a joke, right?\n", id, size); + gfxr_free_font(font); + return NULL; + } + + font->chars_nr = chars_nr = get_int_16(resource + FONT_MAXCHAR_OFFSET); + font->line_height = max_height = get_int_16(resource + FONT_HEIGHT_OFFSET); + + if (chars_nr < 0 || chars_nr > 256 || max_height < 0) { + if (chars_nr < 0 || chars_nr > 256) + GFXERROR("Font %04x: Invalid number of characters: %d\n", id, + chars_nr); + if (max_height < 0) + GFXERROR("Font %04x: Invalid font height: %d\n", id, max_height); + gfxr_free_font(font); + return NULL; + } + + if (size < 6 + chars_nr * 2) { + GFXERROR("Font %04x: Insufficient space for %d characters in font\n", + id, chars_nr); + gfxr_free_font(font); + return NULL; + } + + font->ID = id; + font->widths = (int*)sci_malloc(sizeof(int) * chars_nr); + + for (i = 0; i < chars_nr; i++) { + int offset = get_int_16(resource + (i << 1) + 6); + + if (offset >= size) { + GFXERROR("Font %04x: Error: Character 0x%02x is at offset 0x%04x (beyond 0x%04x)\n", + id, i, offset, size); + gfxr_free_font(font); + return NULL; + } + + if ((resource[offset]) > max_width) + max_width = resource[offset]; + if ((resource[offset + 1]) > max_height) + max_height = resource[offset + 1]; + + font->widths[i] = resource[offset]; + } + + font->height = max_height; + font->row_size = (max_width + 7) >> 3; + + if (font->row_size == 3) + font->row_size = 4; + + if (font->row_size > 4) + font->row_size = (font->row_size + 3) & ~3; + + font->char_size = font->row_size * max_height; + font->data = (byte*)sci_calloc(font->char_size, chars_nr); + + for (i = 0; i < chars_nr; i++) { + int offset = get_int_16(resource + (i << 1) + 6); + + if (calc_char(font->data + (font->char_size * i), font->row_size, max_height, + resource + offset, size - offset)) { + GFXERROR("Problem occured in font %04x, char %d/%d\n", id, i, chars_nr); + gfxr_free_font(font); + return NULL; + } + } + + return font; +} + + diff --git a/engines/sci/gfx/resource/sci_pal_1.c b/engines/sci/gfx/resource/sci_pal_1.c new file mode 100644 index 0000000000..72adb7b02a --- /dev/null +++ b/engines/sci/gfx/resource/sci_pal_1.c @@ -0,0 +1,178 @@ +/*************************************************************************** + pal_1.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* SCI1 palette resource defrobnicator */ + +#include +#include +#include + +#define MAX_COLORS 256 +#define PALETTE_START 260 +#define COLOR_OK 0x01 + +#define SCI_PAL_FORMAT_VARIABLE_FLAGS 0 +#define SCI_PAL_FORMAT_CONSTANT_FLAGS 1 + +gfx_pixmap_color_t * +gfxr_read_pal11(int id, int *colors_nr, byte *resource, int size) +{ + int start_color = resource[25]; + int format = resource[32]; + int entry_size; + gfx_pixmap_color_t *retval; + byte *pal_data = resource + 37; + int _colors_nr = *colors_nr = getUInt16(resource + 29); + int i; + + switch (format) + { + case SCI_PAL_FORMAT_VARIABLE_FLAGS : + entry_size = 4; + break; + case SCI_PAL_FORMAT_CONSTANT_FLAGS : + entry_size = 3; + break; + } + + retval = (gfx_pixmap_color_t *) + sci_malloc(sizeof(gfx_pixmap_color_t) * (_colors_nr + start_color)); + memset(retval, 0, sizeof(gfx_pixmap_color_t) * (_colors_nr + start_color)); + + for (i = 0; i < start_color; i ++) + { + retval[i].global_index = GFX_COLOR_INDEX_UNMAPPED; + retval[i].r = 0; + retval[i].g = 0; + retval[i].b = 0; + } + for (i = start_color; i < start_color + _colors_nr; i ++) + { + switch (format) + { + case SCI_PAL_FORMAT_CONSTANT_FLAGS: + retval[i].global_index = GFX_COLOR_INDEX_UNMAPPED; + retval[i].r = pal_data[0]; + retval[i].g = pal_data[1]; + retval[i].b = pal_data[2]; + break; + case SCI_PAL_FORMAT_VARIABLE_FLAGS: + retval[i].global_index = GFX_COLOR_INDEX_UNMAPPED; + retval[i].r = pal_data[1]; + retval[i].g = pal_data[2]; + retval[i].b = pal_data[3]; + break; + } + pal_data += entry_size; + } + + return retval; +} + +gfx_pixmap_color_t * +gfxr_read_pal1(int id, int *colors_nr, byte *resource, int size) +{ + int counter = 0; + int pos; + unsigned int colors[MAX_COLORS] = {0}; + gfx_pixmap_color_t *retval; + + if (size < PALETTE_START + 4) { + GFXERROR("Palette resource too small in %04x\n", id); + return NULL; + } + + + pos = PALETTE_START; + + while (pos < size/* && resource[pos] == COLOR_OK && counter < MAX_COLORS*/) { + int color = resource[pos] + | (resource[pos + 1] << 8) + | (resource[pos + 2] << 16) + | (resource[pos + 3] << 24); + + pos += 4; + + colors[counter++] = color; + } + + if (counter < MAX_COLORS && resource[pos] != COLOR_OK) { + GFXERROR("Palette %04x uses unknown palette color prefix 0x%02x at offset 0x%04x\n", id, resource[pos], pos); + return NULL; + } + + if (counter < MAX_COLORS) { + GFXERROR("Palette %04x ends prematurely\n", id); + return NULL; + } + + retval = (gfx_pixmap_color_t*)sci_malloc(sizeof(gfx_pixmap_color_t) * counter); +#ifdef SATISFY_PURIFY + memset(retval, 0, sizeof(gfx_pixmap_color_t) * counter); +#endif + + *colors_nr = counter; + for (pos = 0; pos < counter; pos++) { + unsigned int color = colors[pos]; + + retval[pos].global_index = GFX_COLOR_INDEX_UNMAPPED; + retval[pos].r = (color >> 8) & 0xff; + retval[pos].g = (color >> 16) & 0xff; + retval[pos].b = (color >> 24) & 0xff; + } + + return retval; +} + +gfx_pixmap_color_t * +gfxr_read_pal1_amiga(int *colors_nr, FILE *f) +{ + int i; + gfx_pixmap_color_t *retval; + + retval = (gfx_pixmap_color_t*)sci_malloc(sizeof(gfx_pixmap_color_t) * 32); + + for (i = 0; i < 32; i++) { + int b1, b2; + + b1 = fgetc(f); + b2 = fgetc(f); + + if (b1 == EOF || b2 == EOF) { + GFXERROR("Palette file ends prematurely\n"); + return NULL; + } + + retval[i].global_index = GFX_COLOR_INDEX_UNMAPPED; + retval[i].r = (b1 & 0xf) * 0x11; + retval[i].g = ((b2 & 0xf0) >> 4) * 0x11; + retval[i].b = (b2 & 0xf) * 0x11; + } + + *colors_nr = 32; + return retval; +} + diff --git a/engines/sci/gfx/resource/sci_pic_0.c b/engines/sci/gfx/resource/sci_pic_0.c new file mode 100644 index 0000000000..63130b53f4 --- /dev/null +++ b/engines/sci/gfx/resource/sci_pic_0.c @@ -0,0 +1,2023 @@ +/*************************************************************************** + sci_pic_0.c Copyright (C) 2000 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#undef GFXR_DEBUG_PIC0 /* Enable to debug pic0 messages */ +#undef FILL_RECURSIVE_DEBUG /* Enable for verbose fill debugging */ + +#define GFXR_PIC0_PALETTE_SIZE 40 +#define GFXR_PIC0_NUM_PALETTES 4 + +#define INTERCOL(a, b) ((int) sqrt((((3.3 * (a))*(a)) + ((1.7 * (b))*(b))) / 5.0)) +/* Macro for color interpolation */ + +#define SCI_PIC0_MAX_FILL 30 /* Number of times to fill before yielding to scheduler */ + +#define SCI0_MAX_PALETTE 2 + +int sci0_palette = 0; + +/* Copied from include/kernel.h */ +#define SCI0_PRIORITY_BAND_FIRST_14_ZONES(nr) ((((nr) == 0)? 0 : \ + ((first) + (((nr)-1) * (last - first)) / 14))) + +/* Default color maps */ +gfx_pixmap_color_t gfx_sci0_image_colors[SCI0_MAX_PALETTE+1][GFX_SCI0_IMAGE_COLORS_NR] = { + {{GFX_COLOR_SYSTEM, 0x00, 0x00, 0x00}, {GFX_COLOR_SYSTEM, 0x00, 0x00, 0xaa}, + {GFX_COLOR_SYSTEM, 0x00, 0xaa, 0x00}, {GFX_COLOR_SYSTEM, 0x00, 0xaa, 0xaa}, + {GFX_COLOR_SYSTEM, 0xaa, 0x00, 0x00}, {GFX_COLOR_SYSTEM, 0xaa, 0x00, 0xaa}, + {GFX_COLOR_SYSTEM, 0xaa, 0x55, 0x00}, {GFX_COLOR_SYSTEM, 0xaa, 0xaa, 0xaa}, + {GFX_COLOR_SYSTEM, 0x55, 0x55, 0x55}, {GFX_COLOR_SYSTEM, 0x55, 0x55, 0xff}, + {GFX_COLOR_SYSTEM, 0x55, 0xff, 0x55}, {GFX_COLOR_SYSTEM, 0x55, 0xff, 0xff}, + {GFX_COLOR_SYSTEM, 0xff, 0x55, 0x55}, {GFX_COLOR_SYSTEM, 0xff, 0x55, 0xff}, + {GFX_COLOR_SYSTEM, 0xff, 0xff, 0x55}, {GFX_COLOR_SYSTEM, 0xff, 0xff, 0xff}}, /* "Normal" EGA */ + + + {{GFX_COLOR_SYSTEM, 0x00, 0x00, 0x00}, {GFX_COLOR_SYSTEM, 0x00, 0x00, 0xff}, + {GFX_COLOR_SYSTEM, 0x00, 0xaa, 0x00}, {GFX_COLOR_SYSTEM, 0x00, 0xaa, 0xaa}, + {GFX_COLOR_SYSTEM, 0xce, 0x00, 0x00}, {GFX_COLOR_SYSTEM, 0xbe, 0x71, 0xde}, + {GFX_COLOR_SYSTEM, 0x8d, 0x50, 0x00}, {GFX_COLOR_SYSTEM, 0xbe, 0xbe, 0xbe}, + {GFX_COLOR_SYSTEM, 0x55, 0x55, 0x55}, {GFX_COLOR_SYSTEM, 0x00, 0xbe, 0xff}, + {GFX_COLOR_SYSTEM, 0x00, 0xce, 0x55}, {GFX_COLOR_SYSTEM, 0x55, 0xff, 0xff}, + {GFX_COLOR_SYSTEM, 0xff, 0x9d, 0x8d}, {GFX_COLOR_SYSTEM, 0xff, 0x55, 0xff}, + {GFX_COLOR_SYSTEM, 0xff, 0xff, 0x00}, {GFX_COLOR_SYSTEM, 0xff, 0xff, 0xff}}, /* AGI Amiga-ish */ + +/* RGB and I intensities (former taken from the GIMP) */ +#define GR 30 +#define GG 59 +#define GB 11 +#define GI 15 + +#define FULL (GR+GG+GB+GI) + +#define CC(x) (((x)*255)/FULL),(((x)*255)/FULL),(((x)*255)/FULL) /* Combines color intensities */ + + {{GFX_COLOR_SYSTEM, CC(0) }, {GFX_COLOR_SYSTEM, CC(GB) }, + {GFX_COLOR_SYSTEM, CC(GG) }, {GFX_COLOR_SYSTEM, CC(GB+GG) }, + {GFX_COLOR_SYSTEM, CC(GR) }, {GFX_COLOR_SYSTEM, CC(GB+GR) }, + {GFX_COLOR_SYSTEM, CC(GG+GR) }, {GFX_COLOR_SYSTEM, CC(GB+GG+GR) }, + {GFX_COLOR_SYSTEM, CC(GI) }, {GFX_COLOR_SYSTEM, CC(GB+GI) }, + {GFX_COLOR_SYSTEM, CC(GG+GI) }, {GFX_COLOR_SYSTEM, CC(GB+GG+GI) }, + {GFX_COLOR_SYSTEM, CC(GR+GI) }, {GFX_COLOR_SYSTEM, CC(GB+GR+GI) }, + {GFX_COLOR_SYSTEM, CC(GG+GR+GI) }, {GFX_COLOR_SYSTEM, CC(GB+GG+GR+GI) }}}; /* Grayscale */ + +#undef GR +#undef GG +#undef GB +#undef GI + +#undef FULL + +#undef C2 +#undef C3 +#undef C4 + +gfx_pixmap_color_t gfx_sci0_pic_colors[GFX_SCI0_PIC_COLORS_NR]; /* Initialized during initialization */ + +static int _gfxr_pic0_colors_initialized = 0; + +#define SCI1_PALETTE_SIZE 1284 + +#ifdef FILL_RECURSIVE_DEBUG +/************************************/ +int fillc = 100000000; +int fillmagc = 30000000; +/************************************/ +#endif + +/* Color mapping used while scaling embedded views. */ +gfx_pixmap_color_t embedded_view_colors[16] = { + {0x00, 0, 0, 0}, {0x11, 0, 0, 0}, {0x22, 0, 0, 0}, {0x33, 0, 0, 0}, + {0x44, 0, 0, 0}, {0x55, 0, 0, 0}, {0x66, 0, 0, 0}, {0x77, 0, 0, 0}, + {0x88, 0, 0, 0}, {0x99, 0, 0, 0}, {0xaa, 0, 0, 0}, {0xbb, 0, 0, 0}, + {0xcc, 0, 0, 0}, {0xdd, 0, 0, 0}, {0xee, 0, 0, 0}, {0xff, 0, 0, 0} +}; + +void +gfxr_init_static_palette() +{ + int i; + + if (!_gfxr_pic0_colors_initialized) { + for (i = 0; i < 256; i++) { + gfx_sci0_pic_colors[i].global_index = GFX_COLOR_INDEX_UNMAPPED; + gfx_sci0_pic_colors[i].r = INTERCOL(gfx_sci0_image_colors[sci0_palette][i & 0xf].r, + gfx_sci0_image_colors[sci0_palette][i >> 4].r); + gfx_sci0_pic_colors[i].g = INTERCOL(gfx_sci0_image_colors[sci0_palette][i & 0xf].g, + gfx_sci0_image_colors[sci0_palette][i >> 4].g); + gfx_sci0_pic_colors[i].b = INTERCOL(gfx_sci0_image_colors[sci0_palette][i & 0xf].b, + gfx_sci0_image_colors[sci0_palette][i >> 4].b); + } + WARNING("Uncomment me after fixing sci0_palette changes to reset me"); + /* _gfxr_pic0_colors_initialized = 1; */ + } +} + + +gfxr_pic_t * +gfxr_init_pic(gfx_mode_t *mode, int ID, int sci1) +{ + gfxr_pic_t *pic = (gfxr_pic_t*)sci_malloc(sizeof(gfxr_pic_t)); + + pic->mode = mode; + + pic->control_map = gfx_pixmap_alloc_index_data(gfx_new_pixmap(320, 200, ID, 2, 0)); + + pic->priority_map = gfx_pixmap_alloc_index_data(gfx_new_pixmap(mode->xfact * 320, mode->yfact * 200, + ID, 1, 0)); + + + pic->visual_map = gfx_pixmap_alloc_index_data(gfx_new_pixmap(320 * mode->xfact, + 200 * mode->yfact, ID, 0, 0)); + pic->visual_map->colors = gfx_sci0_pic_colors; + pic->visual_map->colors_nr = GFX_SCI0_PIC_COLORS_NR; + pic->visual_map->color_key = GFX_PIXMAP_COLOR_KEY_NONE; + + pic->visual_map->flags = GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + pic->priority_map->flags = GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + pic->control_map->flags = GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + if (mode->xfact > 1 || mode->yfact > 1) { + pic->visual_map->flags |= GFX_PIXMAP_FLAG_SCALED_INDEX; + pic->priority_map->flags |= GFX_PIXMAP_FLAG_SCALED_INDEX; + } + + pic->priority_map->colors = gfx_sci0_image_colors[sci0_palette]; + pic->priority_map->colors_nr = GFX_SCI0_IMAGE_COLORS_NR; + pic->control_map->colors = gfx_sci0_image_colors[sci0_palette]; + pic->control_map->colors_nr = GFX_SCI0_IMAGE_COLORS_NR; + + /* Initialize colors */ + if (!sci1) { + pic->ID = ID; + gfxr_init_static_palette(); + } + + pic->undithered_buffer_size = pic->visual_map->index_xl * pic->visual_map->index_yl; + pic->undithered_buffer = NULL; + pic->internal = NULL; + + return pic; +} + + +/****************************/ +/* Pic rendering operations */ +/****************************/ + +void +gfxr_clear_pic0(gfxr_pic_t *pic, int sci_titlebar_size) +{ + memset(pic->visual_map->index_data, 0x00, (320 * pic->mode->xfact * sci_titlebar_size * pic->mode->yfact)); + memset(pic->visual_map->index_data + (320 * pic->mode->xfact * sci_titlebar_size * pic->mode->yfact), + 0xff, pic->mode->xfact * 320 * pic->mode->yfact * (200 - sci_titlebar_size)); /* white */ + memset(pic->priority_map->index_data + (320 * pic->mode->xfact * sci_titlebar_size * pic->mode->yfact), + 0x0, pic->mode->xfact * 320 * pic->mode->yfact * (200 - sci_titlebar_size)); + memset(pic->priority_map->index_data, 0x0a, sci_titlebar_size * (pic->mode->yfact * 320 * pic->mode->xfact)); + memset(pic->control_map->index_data, 0, GFXR_AUX_MAP_SIZE); + memset(pic->aux_map, 0, GFXR_AUX_MAP_SIZE); +} + + +/*** Basic operations on the auxiliary buffer ***/ + +#define FRESH_PAINT 0x40 +/* freshly filled or near to something that is */ + +#define LINEMACRO(startx, starty, deltalinear, deltanonlinear, linearvar, nonlinearvar, \ + linearend, nonlinearstart, linearmod, nonlinearmod, operation) \ + x = (startx); y = (starty); \ + incrNE = ((deltalinear) > 0)? (deltalinear) : -(deltalinear); \ + incrNE <<= 1; \ + deltanonlinear <<= 1; \ + incrE = ((deltanonlinear) > 0) ? -(deltanonlinear) : (deltanonlinear); \ + d = nonlinearstart-1; \ + while (linearvar != (linearend)) { \ + buffer[linewidth * y + x] operation color; \ +/* color ^= color2; color2 ^= color; color ^= color2; */ /* Swap colors */ \ + linearvar += linearmod; \ + if ((d+=incrE) < 0) { \ + d += incrNE; \ + nonlinearvar += nonlinearmod; \ + }; \ + }; \ + buffer[linewidth * y + x] operation color; + +static void +_gfxr_auxbuf_line_draw(gfxr_pic_t *pic, rect_t line, int color, int color2, int sci_titlebar_size) +{ + int dx, dy, incrE, incrNE, d, finalx, finaly; + int x = line.x; + int y = line.y + sci_titlebar_size; + unsigned char *buffer = pic->aux_map; + int linewidth = 320; + + dx = line.xl; + dy = line.yl; + finalx = x + dx; + finaly = y + dy; + + dx = abs(dx); + dy = abs(dy); + + if (dx > dy) { + if (finalx < x) { + if (finaly < y) { /* llu == left-left-up */ + LINEMACRO(x, y, dx, dy, x, y, finalx, dx, -1, -1, |=); + } else { /* lld */ + LINEMACRO(x, y, dx, dy, x, y, finalx, dx, -1, 1, |=); + } + } else { /* x1 >= x */ + if (finaly < y) { /* rru */ + LINEMACRO(x, y, dx, dy, x, y, finalx, dx, 1, -1, |=); + } else { /* rrd */ + LINEMACRO(x, y, dx, dy, x, y, finalx, dx, 1, 1, |=); + } + } + } else { /* dx <= dy */ + if (finaly < y) { + if (finalx < x) { /* luu */ + LINEMACRO(x, y, dy, dx, y, x, finaly, dy, -1, -1, |=); + } else { /* ruu */ + LINEMACRO(x, y, dy, dx, y, x, finaly, dy, -1, 1, |=); + } + } else { /* y1 >= y */ + if (finalx < x) { /* ldd */ + LINEMACRO(x, y, dy, dx, y, x, finaly, dy, 1, -1, |=); + } else { /* rdd */ + LINEMACRO(x, y, dy, dx, y, x, finaly, dy, 1, 1, |=); + } + } + } +} + +static void +_gfxr_auxbuf_line_clear(gfxr_pic_t *pic, rect_t line, int color, int sci_titlebar_size) +{ + int dx, dy, incrE, incrNE, d, finalx, finaly; + int x = line.x; + int y = line.y + sci_titlebar_size; + unsigned char *buffer = pic->aux_map; + int linewidth = 320; + int color2 = color; + + dx = line.xl; + dy = line.yl; + finalx = x + dx; + finaly = y + dy; + + dx = abs(dx); + dy = abs(dy); + + if (dx > dy) { + if (finalx < x) { + if (finaly < y) { /* llu == left-left-up */ + LINEMACRO(x, y, dx, dy, x, y, finalx, dx, -1, -1, &=); + } else { /* lld */ + LINEMACRO(x, y, dx, dy, x, y, finalx, dx, -1, 1, &=); + } + } else { /* x1 >= x */ + if (finaly < y) { /* rru */ + LINEMACRO(x, y, dx, dy, x, y, finalx, dx, 1, -1, &=); + } else { /* rrd */ + LINEMACRO(x, y, dx, dy, x, y, finalx, dx, 1, 1, &=); + } + } + } else { /* dx <= dy */ + if (finaly < y) { + if (finalx < x) { /* luu */ + LINEMACRO(x, y, dy, dx, y, x, finaly, dy, -1, -1, &=); + } else { /* ruu */ + LINEMACRO(x, y, dy, dx, y, x, finaly, dy, -1, 1, &=); + } + } else { /* y1 >= y */ + if (finalx < x) { /* ldd */ + LINEMACRO(x, y, dy, dx, y, x, finaly, dy, 1, -1, &=); + } else { /* rdd */ + LINEMACRO(x, y, dy, dx, y, x, finaly, dy, 1, 1, &=); + } + } + } +} + +#undef LINEMACRO + + +#ifdef WITH_PIC_SCALING +static void +_gfxr_auxbuf_propagate_changes(gfxr_pic_t *pic, int bitmask) +{ + /* Propagates all filled bits into the planes described by bitmask */ + unsigned long *data = (unsigned long *) pic->aux_map; + unsigned long clearmask = 0x07070707; + unsigned long andmask = + (bitmask << 3) + | (bitmask << (3+8)) + | (bitmask << (3+16)) + | (bitmask << (3+24)); + int i; + + if (sizeof(unsigned long) == 8) { /* UltraSparc, Alpha, newer MIPSens, etc */ + andmask |= (andmask << 32); + clearmask |= (clearmask << 32); + } + + for (i = 0; i < GFXR_AUX_MAP_SIZE / sizeof(unsigned long); i++) { + unsigned long temp = *data & andmask; + temp >>= 3; + *data = (temp | *data) & clearmask; + ++data; + } +} +#endif + + +static inline void +_gfxr_auxbuf_tag_line(gfxr_pic_t *pic, int pos, int width) +{ + int i; + for (i = 0; i < width; i++) + pic->aux_map[i+pos] |= FRESH_PAINT; +} + + +static void +_gfxr_auxbuf_spread(gfxr_pic_t *pic, int *min_x, int *min_y, int *max_x, int *max_y) +{ + /* Tries to spread by approximating the first derivation of the border function. + ** Draws to the current and the last line, thus taking up to twice as long as neccessary. + ** Other than that, it's O(n^2) + */ + + int intervals_nr = 0, old_intervals_nr; + int x, y, i, pos = 10*320; + struct interval_struct { + int xl, xr, tag; + } intervals[2][160]; + + *max_x = *max_y = -1; + *min_x = *min_y = 320; + +#ifdef FILL_RECURSIVE_DEBUG + if (!fillmagc) { + fprintf(stderr,"------------------------------------------------\n"); + fprintf(stderr,"LineID: "); + for (i = 0; i < 5; i++) + fprintf(stderr," %d ", i); + fprintf(stderr,"\n"); + } +#endif + + for (y = 10; y < 200; y++) { + int ivi = y & 1; /* InterVal Index: Current intervals; !ivi is the list of old ones */ + int old_intervals_start_offset = 0; + int width = 0; + + old_intervals_nr = intervals_nr; + intervals_nr = 0; + + for (x = 0; x < 321; x++) + if (x < 320 && pic->aux_map[pos+x] & 0x10) + width++; + else if (width) { /* Found one interval */ + int xl = x - width; + int xr = x - 1; + int done = 0; + int found_interval = 0; + + intervals[ivi][intervals_nr].xl = xl; + intervals[ivi][intervals_nr].tag = 0; + intervals[ivi][intervals_nr++].xr = xr; + + if (xl < *min_x) + *min_x = xl; + if (xr > *max_x) + *max_x = xr; + + i = old_intervals_start_offset; + while (!done && i < old_intervals_nr) { + if (intervals[!ivi][i].xl > xr+1) + done = 1; + + else if (intervals[!ivi][i].xr < xl-1) { + int o_xl = intervals[!ivi][i].xl; + int o_xr = intervals[!ivi][i].xr; + if (o_xr == o_xl && !intervals[!ivi][i].tag) { /* thin bar */ + memcpy(intervals[ivi] + intervals_nr, intervals[ivi] + intervals_nr - 1, sizeof(struct interval_struct)); + memcpy(intervals[ivi] + intervals_nr - 1, intervals[!ivi] + i, sizeof(struct interval_struct)); + intervals[!ivi][i].tag = 1; + pic->aux_map[pos - 320 + o_xl] |= FRESH_PAINT; + ++intervals_nr; + } + + old_intervals_start_offset = i; + } + + else { + int k = i; + int old_xl = intervals[!ivi][i].xl; + int dwidth_l = abs(old_xl - xl); + int old_xr, dwidth_r; + int write_left_width, write_right_width; + + intervals[!ivi][i].tag = 1; + while (k+1 < old_intervals_nr && intervals[!ivi][k+1].xl <= xr) { + ++k; + intervals[!ivi][i].tag = 1; + } + + old_xr = intervals[!ivi][k].xr; + dwidth_r = abs(old_xr - xr); + + /* Current line */ + write_left_width = (dwidth_l > xl)? xl : dwidth_l; + _gfxr_auxbuf_tag_line(pic, pos + xl - write_left_width, write_left_width); + + write_right_width = (dwidth_r + xr > 319)? 320 - xr : dwidth_r; + _gfxr_auxbuf_tag_line(pic, pos + xr, write_right_width); + + if (xl - write_left_width < *min_x) + *min_x = xl - write_left_width; + if (xr + write_right_width > *max_x) + *max_x = xr + write_right_width; + + /* Previous line */ + write_left_width = (dwidth_l > old_xl)? old_xl : dwidth_l; + write_right_width = (dwidth_r + old_xr > 319)? 320 - old_xr : dwidth_r; + + if (i == k) { /* Only one predecessor interval */ + _gfxr_auxbuf_tag_line(pic, pos - 320 + old_xl - write_left_width, write_left_width); + _gfxr_auxbuf_tag_line(pic, pos - 320 + old_xr, write_right_width); + } else /* Fill entire line */ + _gfxr_auxbuf_tag_line(pic, pos - 320 + old_xl - write_left_width, old_xr - old_xl + + 1 + write_left_width + write_right_width); + + if (xl - write_left_width < *min_x) + *min_x = xl - write_left_width; + if (xr + write_right_width > *max_x) + *max_x = xr + write_right_width; + + found_interval = done = 1; + } + i++; + } + width = 0; + } + +#ifdef FILL_RECURSIVE_DEBUG + if (!fillmagc && intervals_nr) { + fprintf(stderr,"AI L#%03d:", y); + for (int j = 0; j < intervals_nr; j++) + fprintf(stderr, "%c[%03d,%03d]", intervals[ivi][j].tag? ' ':'-', intervals[ivi][j].xl, intervals[ivi][j].xr); + fprintf(stderr,"\n"); + } +#endif + + if (intervals_nr) { + if (y < *min_y) + *min_y = y; + *max_y = y; + } + + pos += 320; + } + + for (pos = 320*200 - 1; pos >= 320; pos--) + if (pic->aux_map[pos - 320] & 0x40) + pic->aux_map[pos] |= 0x40; + + if (*max_y < 199) + (*max_y)++; +} + + + +/*** Regular drawing operations ***/ + + +#define PATTERN_FLAG_RECTANGLE 0x10 +#define PATTERN_FLAG_USE_PATTERN 0x20 + +#define PIC_OP_FIRST 0xf0 + +enum { + PIC_OP_SET_COLOR = 0xf0, + PIC_OP_DISABLE_VISUAL = 0xf1, + PIC_OP_SET_PRIORITY = 0xf2, + PIC_OP_DISABLE_PRIORITY = 0xf3, + PIC_OP_SHORT_PATTERNS = 0xf4, + PIC_OP_MEDIUM_LINES = 0xf5, + PIC_OP_LONG_LINES = 0xf6, + PIC_OP_SHORT_LINES = 0xf7, + PIC_OP_FILL = 0xf8, + PIC_OP_SET_PATTERN = 0xf9, + PIC_OP_ABSOLUTE_PATTERN = 0xfa, + PIC_OP_SET_CONTROL = 0xfb, + PIC_OP_DISABLE_CONTROL = 0xfc, + PIC_OP_MEDIUM_PATTERNS = 0xfd, + PIC_OP_OPX = 0xfe, + PIC_OP_TERMINATE = 0xff +}; + +enum { + PIC_SCI0_OPX_SET_PALETTE_ENTRIES = 0, + PIC_SCI0_OPX_SET_PALETTE = 1, + PIC_SCI0_OPX_MONO0 = 2, + PIC_SCI0_OPX_MONO1 = 3, + PIC_SCI0_OPX_MONO2 = 4, + PIC_SCI0_OPX_MONO3 = 5, + PIC_SCI0_OPX_MONO4 = 6, + PIC_SCI0_OPX_EMBEDDED_VIEW, + PIC_SCI0_OPX_SET_PRIORITY_TABLE +}; + +/* We use this so we can keep OPX handling in one switch. + We simply add this constant to the op number if we're running an SCI1 game, + and offset the OPX constants below correspondingly. */ +#define SCI1_OP_OFFSET 42 + +enum { + PIC_SCI1_OPX_SET_PALETTE_ENTRIES = 0+SCI1_OP_OFFSET, + PIC_SCI1_OPX_EMBEDDED_VIEW = 1+SCI1_OP_OFFSET, + PIC_SCI1_OPX_SET_PALETTE = 2+SCI1_OP_OFFSET, + PIC_SCI1_OPX_PRIORITY_TABLE_EQDIST = 3+SCI1_OP_OFFSET, + PIC_SCI1_OPX_PRIORITY_TABLE_EXPLICIT = 4+SCI1_OP_OFFSET +}; + + +#ifdef GFXR_DEBUG_PIC0 +#define p0printf sciprintf +#else +#define p0printf if (0) +#endif + + +enum { + ELLIPSE_SOLID, /* Normal filled ellipse */ + ELLIPSE_OR /* color ORred to the buffer */ +}; + +static void +_gfxr_fill_ellipse(gfxr_pic_t *pic, byte *buffer, int linewidth, int x, int y, + int rad_x, int rad_y, int color, int fillstyle) +{ + int xx = 0, yy = rad_y; + int i, x_i, y_i; + int xr = 2 * rad_x * rad_x; + int yr = 2 * rad_y * rad_y; + + x_i = 1; + y_i = xr * rad_y -1; + i = y_i >> 1; + + while (yy >= 0) { + int oldxx = xx; + int oldyy = yy; + + if (i >= 0) { + x_i += yr; + i -= x_i + 1; + ++xx; + } + + if (i < 0) { + y_i -= xr; + i += y_i - 1; + --yy; + } + + if (oldyy != yy) { + int j; + int offset0 = (y-oldyy) * linewidth; + int offset1 = (y+oldyy) * linewidth; + + offset0 += x-oldxx; + offset1 += x-oldxx; + + if (oldyy == 0) + offset1 = 0; /* We never have to draw ellipses in the menu bar */ + + oldyy = yy; + + switch (fillstyle) { + + case ELLIPSE_SOLID: + memset(buffer + offset0, color, (oldxx << 1) + 1); + if (offset1) + memset(buffer + offset1, color, (oldxx << 1) + 1); + break; + + case ELLIPSE_OR: + for (j=0; j < (oldxx << 1) + 1; j++) { + buffer[offset0 + j] |= color; + if (offset1) + buffer[offset1 + j] |= color; + } + break; + + default: + fprintf(stderr,"%s L%d: Invalid ellipse fill mode!\n", __FILE__, __LINE__); + return; + + } + } + } +} + +static inline void +_gfxr_auxplot_brush(gfxr_pic_t *pic, byte *buffer, int yoffset, int offset, int plot, + int color, gfx_brush_mode_t brush_mode, int randseed) +{ + /* yoffset 63680, offset 320, plot 1, color 34, brush_mode 0, randseed 432)*/ + /* Auxplot: Used by plot_aux_pattern to plot to visual and priority */ + int xc, yc; + int line_width = 320 * pic->mode->xfact; + int full_offset = (yoffset * pic->mode->yfact + offset) * pic->mode->xfact; + + if (yoffset + offset >= 64000) { + BREAKPOINT(); + } + + switch (brush_mode) { + case GFX_BRUSH_MODE_SCALED: + if (plot) + for (yc = 0; yc < pic->mode->yfact; yc++) { + memset(buffer + full_offset, color, pic->mode->xfact); + full_offset += line_width; + } + break; + + case GFX_BRUSH_MODE_ELLIPSES: + if (plot) { + int x = offset * pic->mode->xfact + ((pic->mode->xfact -1) >> 1); + int y = (yoffset / 320) * pic->mode->yfact + ((pic->mode->yfact -1) >> 1); /* Ouch! */ + + _gfxr_fill_ellipse(pic, buffer, line_width, x, y, pic->mode->xfact >> 1, pic->mode->yfact >> 1, + color, ELLIPSE_SOLID); + } + break; + + case GFX_BRUSH_MODE_RANDOM_ELLIPSES: + if (plot) { + int x = offset * pic->mode->xfact + ((pic->mode->xfact -1) >> 1); + int y = (yoffset / 320) * pic->mode->yfact + ((pic->mode->yfact -1) >> 1); /* Ouch! */ + int sizex = pic->mode->xfact >> 1; + int sizey = pic->mode->yfact >> 1; + + srand(randseed); + + x -= (int) ((sizex * rand()*1.0)/(RAND_MAX + 1.0)); + x += (int) ((sizex * rand()*1.0)/(RAND_MAX + 1.0)); + y -= (int) ((sizey * rand()*1.0)/(RAND_MAX + 1.0)); + y += (int) ((sizey * rand()*1.0)/(RAND_MAX + 1.0)); + sizex = (int) ((sizex * rand()*1.0)/(RAND_MAX + 1.0)); + sizey = (int) ((sizey * rand()*1.0)/(RAND_MAX + 1.0)); + + _gfxr_fill_ellipse(pic, buffer, line_width, x, y, pic->mode->xfact >> 1, pic->mode->yfact >> 1, + color, ELLIPSE_SOLID); + srand(time(NULL)); /* Make sure we don't accidently forget to re-init the random number generator */ + } + break; + + case GFX_BRUSH_MODE_MORERANDOM: { + int mask = plot? 7 : 1; + srand(randseed); + for (yc = 0; yc < pic->mode->yfact; yc++) { + for (xc = 0; xc < pic->mode->xfact; xc++) + if ((rand() & 7) < mask) + buffer[full_offset + xc] = color; + full_offset += line_width; + } + srand(time(NULL)); /* Make sure we don't accidently forget to re-init the random number generator */ + } + break; + } +} + +#define PLOT_AUX_PATTERN_NO_RANDOM -1 + +static void +_gfxr_plot_aux_pattern(gfxr_pic_t *pic, int x, int y, int size, int circle, int random, + int mask, int color, int priority, int control, + gfx_brush_mode_t brush_mode, int map_nr) +{ + /* Plots an appropriate pattern to the aux buffer and the control buffer, + ** if mask & GFX_MASK_CONTROL + ** random should be set to the random index, or -1 to disable + */ + + /* These circle offsets uniquely identify the circles used by Sierra: */ + int circle_data[][8] = { + {0}, + {1, 0}, + {2, 2, 1}, + {3, 3, 2, 1}, + {4, 4, 4, 3, 1}, + {5, 5, 4, 4, 3, 1}, + {6, 6, 6, 5, 5, 4, 2}, + {7, 7, 7, 6, 6, 5, 4, 2}}; + + /* 'Random' fill patterns, provided by Carl Muckenhoupt: */ + byte random_data[32] = { + 0x20, 0x94, 0x02, 0x24, 0x90, 0x82, 0xa4, 0xa2, 0x82, 0x09, 0x0a, 0x22, + 0x12, 0x10, 0x42, 0x14, 0x91, 0x4a, 0x91, 0x11, 0x08, 0x12, 0x25, 0x10, + 0x22, 0xa8, 0x14, 0x24, 0x00, 0x50, 0x24, 0x04}; + + /* 'Random' fill offsets, provided by Carl Muckenhoupt: */ + byte random_offset[128] = { + 0x00, 0x18, 0x30, 0xc4, 0xdc, 0x65, 0xeb, 0x48, + 0x60, 0xbd, 0x89, 0x05, 0x0a, 0xf4, 0x7d, 0x7d, + 0x85, 0xb0, 0x8e, 0x95, 0x1f, 0x22, 0x0d, 0xdf, + 0x2a, 0x78, 0xd5, 0x73, 0x1c, 0xb4, 0x40, 0xa1, + 0xb9, 0x3c, 0xca, 0x58, 0x92, 0x34, 0xcc, 0xce, + 0xd7, 0x42, 0x90, 0x0f, 0x8b, 0x7f, 0x32, 0xed, + 0x5c, 0x9d, 0xc8, 0x99, 0xad, 0x4e, 0x56, 0xa6, + 0xf7, 0x68, 0xb7, 0x25, 0x82, 0x37, 0x3a, 0x51, + 0x69, 0x26, 0x38, 0x52, 0x9e, 0x9a, 0x4f, 0xa7, + 0x43, 0x10, 0x80, 0xee, 0x3d, 0x59, 0x35, 0xcf, + 0x79, 0x74, 0xb5, 0xa2, 0xb1, 0x96, 0x23, 0xe0, + 0xbe, 0x05, 0xf5, 0x6e, 0x19, 0xc5, 0x66, 0x49, + 0xf0, 0xd1, 0x54, 0xa9, 0x70, 0x4b, 0xa4, 0xe2, + 0xe6, 0xe5, 0xab, 0xe4, 0xd2, 0xaa, 0x4c, 0xe3, + 0x06, 0x6f, 0xc6, 0x4a, 0xa4, 0x75, 0x97, 0xe1 + }; + + int offset = 0, width = 0; + int yoffset = (y - size) * 320; + int i; + int random_index = 0; + gfx_pixmap_t *map = NULL; + + switch (map_nr) { + case GFX_MASK_VISUAL: map = pic->visual_map; break; + case GFX_MASK_PRIORITY: map = pic->priority_map; break; + default: map = pic->control_map; break; + } + + if (random >= 0) + random_index = random_offset[random]; + + if (!circle) { + offset = -size; + width = (size << 1) + 2; + } + + for (i = -size; i <= size; i++) { + int j; + int height; + + if (circle) { + offset = circle_data[size][abs(i)]; + height = width = (offset << 1) + 1; + offset = -offset; + } else height = width - 1; + + if (random == PLOT_AUX_PATTERN_NO_RANDOM) { + + if (mask & map_nr) + memset(map->index_data + yoffset + offset + x, control, width); + + if (map_nr == GFX_MASK_CONTROL) + for (j = x; j < x + width; j++) + pic->aux_map[yoffset + offset + j] |= mask; + + } else { /* Semi-Random! */ + for (j = 0; j < height; j++) { + if (random_data[random_index >> 3] & (0x80 >> (random_index & 7))) { + /* The 'seemingly' random decision */ + if (mask & GFX_MASK_CONTROL) + pic->control_map->index_data[yoffset + x + offset + j] = control; + + pic->aux_map[yoffset + x + offset + j] |= mask; + + if (mask & GFX_MASK_VISUAL) + _gfxr_auxplot_brush(pic, pic->visual_map->index_data, + yoffset, x + offset + j, + 1, color, brush_mode, random_index + x); + + if (mask & GFX_MASK_PRIORITY) + _gfxr_auxplot_brush(pic, pic->priority_map->index_data, + yoffset, x + offset + j, + 1, priority, brush_mode, random_index + x); + + } else { + if (mask & GFX_MASK_VISUAL) + _gfxr_auxplot_brush(pic, pic->visual_map->index_data, + yoffset, x + offset + j, + 0, color, brush_mode, random_index + x); + + if (mask & GFX_MASK_PRIORITY) + _gfxr_auxplot_brush(pic, pic->priority_map->index_data, + yoffset, x + offset + j, + 0, priority, brush_mode, random_index + x); + } + random_index = (random_index + 1) & 0xff; + } + } + + yoffset += 320; + } +} + + +static void +_gfxr_draw_pattern(gfxr_pic_t *pic, int x, int y, int color, int priority, int control, int drawenable, + int pattern_code, int pattern_size, int pattern_nr, gfx_brush_mode_t brush_mode, + int sci_titlebar_size) +{ + int xsize = (pattern_size + 1) * pic->mode->xfact - 1; + int ysize = (pattern_size + 1) * pic->mode->yfact - 1; + int scaled_x, scaled_y; + rect_t boundaries; + int max_x = (pattern_code & PATTERN_FLAG_RECTANGLE)? 318 : 319; /* Rectangles' width is size+1 */ + + p0printf(stderr, "Pattern at (%d,%d) size %d, rand=%d, code=%02x\n", x, y, pattern_size, pattern_nr, pattern_code); + + y += sci_titlebar_size; + + if (x - pattern_size < 0) + x = pattern_size; + + if (y - pattern_size < sci_titlebar_size) + y = sci_titlebar_size + pattern_size; + + if (x + pattern_size > max_x) + x = max_x - pattern_size; + + if (y + pattern_size > 199) + y = 199 - pattern_size; + + scaled_x = x * pic->mode->xfact + ((pic->mode->xfact - 1) >> 1); + scaled_y = y * pic->mode->yfact + ((pic->mode->yfact - 1) >> 1); + + if (scaled_x < xsize) + scaled_x = xsize; + + if (scaled_y < ysize + sci_titlebar_size * pic->mode->yfact) + scaled_y = ysize + sci_titlebar_size * pic->mode->yfact; + + if (scaled_x > (320 * pic->mode->xfact) - 1 - xsize) + scaled_x = (320 * pic->mode->xfact) - 1 - xsize; + + if (scaled_y > (200 * pic->mode->yfact) - 1 - ysize) + scaled_y = (200 * pic->mode->yfact) - 1 - ysize; + + if (pattern_code & PATTERN_FLAG_RECTANGLE) { + /* Rectangle */ + boundaries.x = scaled_x - xsize; + boundaries.y = scaled_y - ysize; + boundaries.xl = ((xsize + 1) << 1) + 1; + boundaries.yl = (ysize << 1) + 1; + + + if (pattern_code & PATTERN_FLAG_USE_PATTERN) { + _gfxr_plot_aux_pattern(pic, x, y, pattern_size, 0, pattern_nr, + drawenable, color, priority, + control, brush_mode, GFX_MASK_CONTROL); + } else { + + _gfxr_plot_aux_pattern(pic, x, y, pattern_size, 0, + PLOT_AUX_PATTERN_NO_RANDOM, + drawenable, 0, 0, control, + GFX_BRUSH_MODE_SCALED, + GFX_MASK_CONTROL); + + if (drawenable & GFX_MASK_VISUAL) + gfx_draw_box_pixmap_i(pic->visual_map, boundaries, color); + + if (drawenable & GFX_MASK_PRIORITY) + gfx_draw_box_pixmap_i(pic->priority_map, boundaries, priority); + } + + } else { + /* Circle */ + + if (pattern_code & PATTERN_FLAG_USE_PATTERN) { + + _gfxr_plot_aux_pattern(pic, x, y, pattern_size, 1, pattern_nr, + drawenable, color, priority, + control, brush_mode, GFX_MASK_CONTROL); + } else { + + _gfxr_plot_aux_pattern(pic, x, y, pattern_size, 1, + PLOT_AUX_PATTERN_NO_RANDOM, + drawenable, 0, 0, control, + GFX_BRUSH_MODE_SCALED, + GFX_MASK_CONTROL); + + if (pic->mode->xfact == 1 && pic->mode->yfact == 1) { + + if (drawenable & GFX_MASK_VISUAL) + _gfxr_plot_aux_pattern(pic, x, y, pattern_size, 1, + PLOT_AUX_PATTERN_NO_RANDOM, + drawenable, 0, 0, color, + GFX_BRUSH_MODE_SCALED, + GFX_MASK_VISUAL); + + if (drawenable & GFX_MASK_PRIORITY) + _gfxr_plot_aux_pattern(pic, x, y, pattern_size, 1, + PLOT_AUX_PATTERN_NO_RANDOM, + drawenable, 0, 0, priority, + GFX_BRUSH_MODE_SCALED, + GFX_MASK_PRIORITY); + } else { + + if (drawenable & GFX_MASK_VISUAL) + _gfxr_fill_ellipse(pic, pic->visual_map->index_data, 320 * pic->mode->xfact, + scaled_x, scaled_y, xsize, ysize, + color, ELLIPSE_SOLID); + + if (drawenable & GFX_MASK_PRIORITY) + _gfxr_fill_ellipse(pic, pic->priority_map->index_data, 320 * pic->mode->xfact, + scaled_x, scaled_y, xsize, ysize, + priority, ELLIPSE_SOLID); + } + } + } +} + + +static inline void +_gfxr_draw_subline(gfxr_pic_t *pic, int x, int y, int ex, int ey, int color, int priority, int drawenable) +{ + point_t start; + point_t end; + + start.x = x; + start.y = y; + end.x = ex; + end.y = ey; + + if (ex >= pic->visual_map->index_xl || ey >= pic->visual_map->index_yl || x < 0 || y < 0) { + fprintf(stderr,"While drawing pic0: INVALID LINE %d,%d,%d,%d\n", + GFX_PRINT_POINT(start), GFX_PRINT_POINT(end)); + return; + } + + if (drawenable & GFX_MASK_VISUAL) + gfx_draw_line_pixmap_i(pic->visual_map, start, end, color); + + if (drawenable & GFX_MASK_PRIORITY) + gfx_draw_line_pixmap_i(pic->priority_map, start, end, priority); + +} + +static void +_gfxr_draw_line(gfxr_pic_t *pic, int x, int y, int ex, int ey, int color, + int priority, int control, int drawenable, int line_mode, + int cmd, int sci_titlebar_size) +{ + int scale_x = pic->mode->xfact; + int scale_y = pic->mode->yfact; + int xc, yc; + rect_t line; + int mask; + int partially_white = (drawenable & GFX_MASK_VISUAL) + && (((color & 0xf0) == 0xf0) || ((color & 0x0f) == 0x0f)); + + line.x = x; + line.y = y; + line.xl = ex - x; + line.yl = ey - y; + + if (x > 319 || y > 199 || x < 0 || y < 0 + || ex > 319 || ey > 199 || ex < 0 || ey < 0) { + GFXWARN("While building pic: Attempt to draw line (%d,%d) to (%d,%d): cmd was %d\n", x, y, ex, ey, cmd); + return; + } + + y += sci_titlebar_size; + ey += sci_titlebar_size; + + if (drawenable & GFX_MASK_CONTROL) { + + p0printf(" ctl:%x", control); + gfx_draw_line_pixmap_i(pic->control_map, gfx_point(x, y), gfx_point(x + line.xl, y + line.yl), control); + } + + + /* Calculate everything that is changed to SOLID */ + mask = drawenable & + ( + ((color != 0xff)? 1 : 0) + | ((priority)? 2 : 0) + | ((control)? 4 : 0) + ); + + if (mask) { + int mask2 = mask; + if (partially_white) + mask2 = mask &= ~GFX_MASK_VISUAL; + _gfxr_auxbuf_line_draw(pic, line, mask, mask2, sci_titlebar_size); + } + + /* Calculate everything that is changed to TRANSPARENT */ + mask = drawenable & + ( + ((color == 0xff)? 1 : 0) + | ((!priority)? 2 : 0) + | ((!control)? 4 : 0) + ); + + if (mask) + _gfxr_auxbuf_line_clear(pic, line, ~mask, sci_titlebar_size); + + x *= scale_x; + y *= scale_y; + ex *= scale_x; + ey *= scale_y; + + if (drawenable & GFX_MASK_VISUAL) + p0printf(" col:%02x", color); + + if (drawenable & GFX_MASK_PRIORITY) + p0printf(" pri:%x", priority); + + if (line_mode == GFX_LINE_MODE_FINE) { /* Adjust lines to extend over the full visual */ + x = (x * ((320 + 1) * scale_x - 1)) / (320 * scale_x); + y = (y * ((200 + 1) * scale_y - 1)) / (200 * scale_y); + ex = (ex * ((320 + 1) * scale_x - 1)) / (320 * scale_x); + ey = (ey * ((200 + 1) * scale_y - 1)) / (200 * scale_y); + + _gfxr_draw_subline(pic, x, y, ex, ey, color, priority, drawenable); + } else { + if (x == ex && y == ey) { /* Just one single point? */ + rect_t drawrect; + drawrect.x = x; + drawrect.y = y; + drawrect.xl = scale_x; + drawrect.yl = scale_y; + + if (drawenable & GFX_MASK_VISUAL) + gfx_draw_box_pixmap_i(pic->visual_map, drawrect, color); + + if (drawenable & GFX_MASK_PRIORITY) + gfx_draw_box_pixmap_i(pic->priority_map, drawrect, priority); + + } else { + int width = scale_x; + int height = scale_y; + int x_offset = 0; + int y_offset = 0; + + if (line_mode == GFX_LINE_MODE_FAST) { + width = (width + 1) >> 1; + height = (height + 1) >> 1; + x_offset = (width >> 1); + y_offset = (height >> 1); + } + + for (xc = 0; xc < width; xc++) + _gfxr_draw_subline(pic, + x + xc + x_offset, y + y_offset, + ex + xc + x_offset, ey + y_offset, + color, priority, drawenable); + + if (height > 0) + for (xc = 0; xc < width; xc++) + _gfxr_draw_subline(pic, + x + xc + x_offset, y + height - 1 + y_offset, + ex + xc + x_offset, ey + height - 1 + y_offset, + color, priority, drawenable); + + if (height > 1) { + for (yc = 1; yc < height - 1; yc++) + _gfxr_draw_subline(pic, + x + x_offset, y + yc + y_offset, + ex + x_offset, ey + yc + y_offset, + color, priority, drawenable); + if (width > 0) + for (yc = 1; yc < height - 1; yc++) + _gfxr_draw_subline(pic, + x + width - 1 + x_offset, y + yc + y_offset, + ex + width - 1 + x_offset, ey + yc + y_offset, + color, priority, drawenable); + } + } + } + + p0printf("\n"); +} + + +#define IS_FILL_BOUNDARY(x) (((x) & legalmask) != legalcolor) + + +#ifdef WITH_PIC_SCALING + +#define TEST_POINT(xx, yy) \ + if (pic->aux_map[(yy)*320 + (xx)] & FRESH_PAINT) { \ + mpos = (((yy) * 320 * pic->mode->yfact) + (xx)) * pic->mode->xfact; \ + for (iy = 0; iy < pic->mode->yfact; iy++) { \ + for (ix = 0; ix < pic->mode->xfact; ix++) \ + if (!IS_FILL_BOUNDARY(test_map[mpos + ix])) { \ + *x = ix + (xx) * pic->mode->xfact; \ + *y = iy + (yy) * pic->mode->yfact; \ + return 0; \ + } \ + mpos += linewidth; \ + } \ + } + +static inline int /* returns -1 on failure, 0 on success */ +_gfxr_find_fill_point(gfxr_pic_t *pic, int min_x, int min_y, int max_x, int max_y, int x_320, + int y_200, int color, int drawenable, int *x, int *y) +{ + int linewidth = pic->mode->xfact * 320; + int mpos, ix, iy; + int size_x = (max_x - min_x + 1) >> 1; + int size_y = (max_y - min_y + 1) >> 1; + int mid_x = min_x + size_x; + int mid_y = min_y + size_y; + int max_size = (size_x > size_y)? size_x : size_y; + int size; + int legalcolor; + int legalmask; + byte *test_map; + *x = x_320 * pic->mode->xfact; + *y = y_200 * pic->mode->yfact; + + if (size_x < 0 || size_y < 0) + return 0; + + if (drawenable & GFX_MASK_VISUAL) { + test_map = pic->visual_map->index_data; + + if ((color & 0xf) == 0xf /* When dithering with white, do more + ** conservative checks */ + || (color & 0xf0) == 0xf0) + legalcolor = 0xff; + else + legalcolor = 0xf0; /* Only check the second color */ + + legalmask = legalcolor; + } else if (drawenable & GFX_MASK_PRIORITY) { + test_map = pic->priority_map->index_data; + legalcolor = 0; + legalmask = 0xf; + } else return -3; + + TEST_POINT(x_320, y_200); /* Most likely candidate */ + TEST_POINT(mid_x, mid_y); /* Second most likely candidate */ + + for (size = 1; size <= max_size; size++) { + int i; + + if (size <= size_y) { + int limited_size = (size > size_x)? size_x : size; + + for (i = mid_x - limited_size; i <= mid_x + limited_size; i++) { + TEST_POINT(i, mid_y - size); + TEST_POINT(i, mid_y + size); + } + } + + if (size <= size_x) { + int limited_size = (size - 1 > size_y)? size_y : size - 1; + + for (i = mid_y - limited_size; i <= mid_y + limited_size; i++) { + TEST_POINT(mid_x - size, i); + TEST_POINT(mid_x + size, i); + } + } + } + + return -1; +} + +#undef TEST_POINT + +/* Now include the actual filling code (with scaling support) */ +#define FILL_FUNCTION _gfxr_fill_any +#define FILL_FUNCTION_RECURSIVE _gfxr_fill_any_recursive +#define AUXBUF_FILL_HELPER _gfxr_auxbuf_fill_any_recursive +#define AUXBUF_FILL _gfxr_auxbuf_fill_any +#define DRAW_SCALED +# include "sci_picfill_aux.c" +# include "sci_picfill.c" +#undef DRAW_SCALED +#undef AUXBUF_FILL +#undef AUXBUF_FILL_HELPER +#undef FILL_FUNCTION_RECURSIVE +#undef FILL_FUNCTION + +#endif /* defined(WITH_PIC_SCALING) */ + +/* Include again, but this time without support for scaling */ +#define FILL_FUNCTION _gfxr_fill_1 +#define FILL_FUNCTION_RECURSIVE _gfxr_fill_1_recursive +#define AUXBUF_FILL_HELPER _gfxr_auxbuf_fill_1_recursive +#define AUXBUF_FILL _gfxr_auxbuf_fill_1 +# include "sci_picfill_aux.c" +# include "sci_picfill.c" +#undef AUXBUF_FILL +#undef AUXBUF_FILL_HELPER +#undef FILL_FUNCTION_RECURSIVE +#undef FILL_FUNCTION + + +#define GET_ABS_COORDS(x, y) \ + temp = *(resource + pos++); \ + x = *(resource + pos++); \ + y = *(resource + pos++); \ + x |= (temp & 0xf0) << 4; \ + y |= (temp & 0x0f) << 8; + +#define GET_REL_COORDS(x, y) \ + temp = *(resource + pos++); \ + if (temp & 0x80) \ + x -= ((temp >> 4) & 0x7); \ + else \ + x += (temp >> 4); \ + \ + if (temp & 0x08) \ + y -= (temp & 0x7); \ + else \ + y += (temp & 0x7); + +#define GET_MEDREL_COORDS(oldx, oldy) \ + temp = *(resource + pos++); \ + if (temp & 0x80) \ + y = oldy - (temp & 0x7f); \ + else \ + y = oldy + temp; \ + x = oldx + *((signed char *) resource + pos++); + + +inline static void +check_and_remove_artifact(byte *dest, byte* srcp, int legalcolor, byte l, byte r, byte u, byte d) +{ + if (*dest == legalcolor) { + if (*srcp == legalcolor) + return; + if (l) { + if (srcp[-1] == legalcolor) + return; + if (u && srcp[-320 - 1] == legalcolor) + return; + if (d && srcp[320 - 1] == legalcolor) + return; + } + if (r) { + if (srcp[1] == legalcolor) + return; + if (u && srcp[-320 + 1] == legalcolor) + return; + if (d && srcp[320 + 1] == legalcolor) + return; + } + + if (u && srcp[-320] == legalcolor) + return; + + if (d && srcp[-320] == legalcolor) + return; + + *dest = *srcp; + } +} + + +void +gfxr_remove_artifacts_pic0(gfxr_pic_t *dest, gfxr_pic_t *src) +{ + int x_320, y_200; + int bound_x = dest->mode->xfact; + int bound_y = dest->mode->yfact; + int scaled_line_size = bound_x * 320; + int read_offset = 0; + + assert(src->mode->xfact == 1); + assert(src->mode->yfact == 1); + + if (bound_x == 1 && bound_y == 1) { + /* D'Oh! */ + GFXWARN("attempt to remove artifacts from unscaled pic!\n"); + return; + } + + for (y_200 = 0; y_200 < 200; y_200++) { + for (x_320 = 0; x_320 < 320; x_320++) { + int write_offset = (y_200 * bound_y * scaled_line_size) + (x_320 * bound_x); + int sub_x, sub_y; + byte *src_visualp = &(src->visual_map->index_data[read_offset]); + byte *src_priorityp = &(src->priority_map->index_data[read_offset]); + + for (sub_y = 0; sub_y < bound_y; sub_y++) { + for (sub_x = 0; sub_x < bound_x; sub_x++) { + check_and_remove_artifact(dest->visual_map->index_data + write_offset, + src_visualp, (int)0xff, + (byte)x_320, (byte)(x_320 < 319), (byte)(y_200 > 10), (byte)(y_200 < 199)); + check_and_remove_artifact(dest->priority_map->index_data + write_offset, + src_priorityp, 0, + (byte)x_320, (byte)(x_320 < 319), (byte)(y_200 > 10), (byte)(y_200 < 199)); + ++write_offset; + } + write_offset += scaled_line_size - bound_x; + } + ++read_offset; + } + } + +} + +static void +view_transparentize(gfx_pixmap_t *view, byte *pic_index_data, + int posx, int posy, + int width, int height) +{ + int i,j; + + for (i=0;iindex_data[j*width+i] == view->color_key) + { + view->index_data[j*width+i] = + pic_index_data[(j+posy)*width+i+posx]; + } + } +} + +extern gfx_pixmap_t * +gfxr_draw_cel0(int id, int loop, int cel, byte *resource, int size, gfxr_view_t *view, int mirrored); +extern gfx_pixmap_t * +gfxr_draw_cel1(int id, int loop, int cel, int mirrored, byte *resource, int size, gfxr_view_t *view, int amiga_game); +extern void +_gfx_crossblit_simple(byte *dest, byte *src, int dest_line_width, int src_line_width, int xl, int yl, int bpp); + +void +gfxr_draw_pic01(gfxr_pic_t *pic, int flags, int default_palette, int size, + byte *resource, gfxr_pic0_params_t *style, int resid, int sci1, + gfx_pixmap_color_t *static_pal, int static_pal_nr) +{ + const int default_palette_table[GFXR_PIC0_PALETTE_SIZE] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x88, + 0x88, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x88, + 0x88, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + 0x08, 0x91, 0x2a, 0x3b, 0x4c, 0x5d, 0x6e, 0x88 + }; + + const int default_priority_table[GFXR_PIC0_PALETTE_SIZE] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + int palette[GFXR_PIC0_NUM_PALETTES][GFXR_PIC0_PALETTE_SIZE]; + int priority_table[GFXR_PIC0_PALETTE_SIZE]; + int i; + int drawenable = GFX_MASK_VISUAL | GFX_MASK_PRIORITY; + int priority = 0; + int color = 0; + int pattern_nr = 0; + int pattern_code = 0; + int pattern_size = 0; + int control = 0; + int pos = 0; + int x, y; + int oldx, oldy; + int pal, index; + int temp; + int line_mode = style->line_mode; + int sci_titlebar_size = style->pic_port_bounds.y; + int fill_count = 0; + byte op, opx; + +#ifdef FILL_RECURSIVE_DEBUG + fillmagc = atoi(getenv("FOO")); + fillc = atoi(getenv("FOO2")); +#endif /* FILL_RECURSIVE_DEBUG */ + + /* Initialize palette */ + for (i = 0; i < GFXR_PIC0_NUM_PALETTES; i++) + memcpy(palette[i], default_palette_table, sizeof(int) * GFXR_PIC0_PALETTE_SIZE); + + memcpy(priority_table, default_priority_table, sizeof(int) * GFXR_PIC0_PALETTE_SIZE); + + /* Main loop */ + while (pos < size) { + op = *(resource + pos++); + + switch (op) { + + case PIC_OP_SET_COLOR: + p0printf("Set color @%d\n", pos); + + if (!sci1) { + pal = *(resource + pos++); + index = pal % GFXR_PIC0_PALETTE_SIZE; + pal /= GFXR_PIC0_PALETTE_SIZE; + + pal += default_palette; + + if (pal >= GFXR_PIC0_NUM_PALETTES) { + GFXERROR("Attempt to access invalid palette %d\n", pal); + return; + } + + color = palette[pal][index]; + } else color = *(resource + pos++); + p0printf(" color <- %02x [%d/%d]\n", color, pal, index); + drawenable |= GFX_MASK_VISUAL; + goto end_op_loop; + + + case PIC_OP_DISABLE_VISUAL: + p0printf("Disable visual @%d\n", pos); + drawenable &= ~GFX_MASK_VISUAL; + goto end_op_loop; + + + case PIC_OP_SET_PRIORITY: + p0printf("Set priority @%d\n", pos); + + if (!sci1) { + pal = *(resource + pos++); + index = pal % GFXR_PIC0_PALETTE_SIZE; + pal /= GFXR_PIC0_PALETTE_SIZE; /* Ignore pal */ + + priority = priority_table[index]; + } else priority = *(resource + pos++); + + p0printf(" priority <- %d [%d/%d]\n", priority, pal, index); + drawenable |= GFX_MASK_PRIORITY; + goto end_op_loop; + + + case PIC_OP_DISABLE_PRIORITY: + p0printf("Disable priority @%d\n", pos); + drawenable &= ~GFX_MASK_PRIORITY; + goto end_op_loop; + + + case PIC_OP_SHORT_PATTERNS: + p0printf("Short patterns @%d\n", pos); + if (pattern_code & PATTERN_FLAG_USE_PATTERN) { + pattern_nr = ((*(resource + pos++)) >> 1) & 0x7f; + p0printf(" pattern_nr <- %d\n", pattern_nr); + } + + GET_ABS_COORDS(x, y); + + _gfxr_draw_pattern(pic, x, y, color, priority, control, drawenable, pattern_code, + pattern_size, pattern_nr, style->brush_mode, sci_titlebar_size); + + while (*(resource + pos) < PIC_OP_FIRST) { + if (pattern_code & PATTERN_FLAG_USE_PATTERN) { + pattern_nr = ((*(resource + pos++)) >> 1) & 0x7f; + p0printf(" pattern_nr <- %d\n", pattern_nr); + } + + GET_REL_COORDS(x, y); + + _gfxr_draw_pattern(pic, x, y, color, priority, control, drawenable, pattern_code, + pattern_size, pattern_nr, style->brush_mode, sci_titlebar_size); + } + goto end_op_loop; + + + case PIC_OP_MEDIUM_LINES: + p0printf("Medium lines @%d\n", pos); + GET_ABS_COORDS(oldx, oldy); + while (*(resource + pos) < PIC_OP_FIRST) { +#if 0 + fprintf(stderr,"Medium-line: [%04x] from %d,%d, data %02x %02x (dx=%d)", pos, oldx, oldy, + 0xff & resource[pos], 0xff & resource[pos+1], *((signed char *) resource + pos + 1)); +#endif + GET_MEDREL_COORDS(oldx, oldy); +#if 0 + fprintf(stderr, " to %d,%d\n", x, y); +#endif + _gfxr_draw_line(pic, oldx, oldy, x, y, color, priority, control, drawenable, line_mode, + PIC_OP_MEDIUM_LINES, sci_titlebar_size); + oldx = x; oldy = y; + } + goto end_op_loop; + + + case PIC_OP_LONG_LINES: + p0printf("Long lines @%d\n", pos); + GET_ABS_COORDS(oldx, oldy); + while (*(resource + pos) < PIC_OP_FIRST) { + GET_ABS_COORDS(x,y); + _gfxr_draw_line(pic, oldx, oldy, x, y, color, priority, control, drawenable, line_mode, + PIC_OP_LONG_LINES, sci_titlebar_size); + oldx = x; oldy = y; + } + goto end_op_loop; + + + case PIC_OP_SHORT_LINES: + p0printf("Short lines @%d\n", pos); + GET_ABS_COORDS(oldx, oldy); + x = oldx; y = oldy; + while (*(resource + pos) < PIC_OP_FIRST) { + GET_REL_COORDS(x,y); + _gfxr_draw_line(pic, oldx, oldy, x, y, color, priority, control, drawenable, line_mode, + PIC_OP_SHORT_LINES, sci_titlebar_size); + oldx = x; oldy = y; + } + goto end_op_loop; + + + case PIC_OP_FILL: + p0printf("Fill @%d\n", pos); + while (*(resource + pos) < PIC_OP_FIRST) { + /*fprintf(stderr,"####################\n"); */ + GET_ABS_COORDS(x, y); + p0printf("Abs coords %d,%d\n", x, y); + /*fprintf(stderr,"C=(%d,%d)\n", x, y + sci_titlebar_size);*/ +#ifdef WITH_PIC_SCALING + if (pic->mode->xfact > 1 + || pic->mode->yfact > 1) + _gfxr_fill_any(pic, x, y + sci_titlebar_size, (flags & DRAWPIC01_FLAG_FILL_NORMALLY)? + color : 0, priority, control, drawenable, sci_titlebar_size); + + else +#endif + _gfxr_fill_1(pic, x, y + sci_titlebar_size, (flags & DRAWPIC01_FLAG_FILL_NORMALLY)? + color : 0, priority, control, drawenable, sci_titlebar_size); + + if (fill_count++ > SCI_PIC0_MAX_FILL) { + sci_sched_yield(); + fill_count = 0; + } + +#ifdef FILL_RECURSIVE_DEBUG + if (!fillmagc) { + int x,y; + if (getenv("FOO1")) + for (x = 0; x < 320; x++) + for (y = 0; y < 200; y++) { + int aux = pic->aux_map[x + y*320]; + int pix = (aux & 0xf); + int i; + + if (aux & 0x40) { + if (x == 0 || !(pic->aux_map[x-1 + y * 320] & 0x40)) + for (i = 0; i < pic->mode->yfact; i++) + pic->visual_map->index_data[(x + ((y*pic->mode->yfact)+i)*320) * pic->mode->xfact] ^= 0xff; + + if (x == 319 || !(pic->aux_map[x+1 + y * 320] & 0x40)) + for (i = 0; i < pic->mode->yfact; i++) + pic->visual_map->index_data[pic->mode->xfact - 1 +(x + ((y*pic->mode->yfact)+i)*320) * pic->mode->xfact] ^= 0xff; + + if (y == 0 || !(pic->aux_map[x + (y-1) * 320] & 0x40)) + for (i = 0; i < pic->mode->yfact; i++) + pic->visual_map->index_data[i+(x + ((y*pic->mode->yfact))*320) * pic->mode->xfact] ^= 0xff; + + if (y == 199 || !(pic->aux_map[x + (y+1) * 320] & 0x40)) + for (i = 0; i < pic->mode->yfact; i++) + pic->visual_map->index_data[i+(x + ((y*pic->mode->yfact)+pic->mode->yfact - 1)*320) * pic->mode->xfact] ^= 0xff; + } + + pix |= (aux & 0x40) >> 4; + pix |= (pix << 4); + + pic->visual_map->index_data[x + y*320*pic->mode->xfact] = pix; + } + return; + } --fillmagc; +#endif /* GFXR_DEBUG_PIC0 */ + } + goto end_op_loop; + + + case PIC_OP_SET_PATTERN: + p0printf("Set pattern @%d\n", pos); + pattern_code = (*(resource + pos++)); + pattern_size = pattern_code & 0x07; + goto end_op_loop; + + + case PIC_OP_ABSOLUTE_PATTERN: + p0printf("Absolute pattern @%d\n", pos); + while (*(resource + pos) < PIC_OP_FIRST) { + if (pattern_code & PATTERN_FLAG_USE_PATTERN) { + pattern_nr = ((*(resource + pos++)) >> 1) & 0x7f; + p0printf(" pattern_nr <- %d\n", pattern_nr); + } + + GET_ABS_COORDS(x, y); + + _gfxr_draw_pattern(pic, x, y, color, priority, control, drawenable, pattern_code, + pattern_size, pattern_nr, style->brush_mode, sci_titlebar_size); + } + goto end_op_loop; + + + case PIC_OP_SET_CONTROL: + p0printf("Set control @%d\n", pos); + control = (*(resource + pos++)) & 0xf; + drawenable |= GFX_MASK_CONTROL; + goto end_op_loop; + + + case PIC_OP_DISABLE_CONTROL: + p0printf("Disable control @%d\n", pos); + drawenable &= ~GFX_MASK_CONTROL; + goto end_op_loop; + + + case PIC_OP_MEDIUM_PATTERNS: + p0printf("Medium patterns @%d\n", pos); + if (pattern_code & PATTERN_FLAG_USE_PATTERN) { + pattern_nr = ((*(resource + pos++)) >> 1) & 0x7f; + p0printf(" pattern_nr <- %d\n", pattern_nr); + } + + GET_ABS_COORDS(oldx, oldy); + + _gfxr_draw_pattern(pic, oldx, oldy, color, priority, control, drawenable, pattern_code, + pattern_size, pattern_nr, style->brush_mode, sci_titlebar_size); + + x = oldx; y = oldy; + while (*(resource + pos) < PIC_OP_FIRST) { + if (pattern_code & PATTERN_FLAG_USE_PATTERN) { + pattern_nr = ((*(resource + pos++)) >> 1) & 0x7f; + p0printf(" pattern_nr <- %d\n", pattern_nr); + } + + GET_MEDREL_COORDS(x, y); + + _gfxr_draw_pattern(pic, x, y, color, priority, control, drawenable, pattern_code, + pattern_size, pattern_nr, style->brush_mode, sci_titlebar_size); + } + goto end_op_loop; + + + case PIC_OP_OPX: + opx = *(resource + pos++); + p0printf("OPX: "); + + if (sci1) opx += SCI1_OP_OFFSET; /* See comment at the definition of SCI1_OP_OFFSET. */ + + switch (opx) { + + case PIC_SCI1_OPX_SET_PALETTE_ENTRIES: + GFXWARN("SCI1 Set palette entried not implemented\n"); + goto end_op_loop; + + case PIC_SCI0_OPX_SET_PALETTE_ENTRIES: + p0printf("Set palette entry @%d\n", pos); + while (*(resource + pos) < PIC_OP_FIRST) { + index = *(resource + pos++); + pal = index / GFXR_PIC0_PALETTE_SIZE; + index %= GFXR_PIC0_PALETTE_SIZE; + + if (pal >= GFXR_PIC0_NUM_PALETTES) { + GFXERROR("Attempt to write to invalid palette %d\n", pal); + return; + } + palette[pal][index] = *(resource + pos++); + } + goto end_op_loop; + + + case PIC_SCI0_OPX_SET_PALETTE: + p0printf("Set palette @%d\n", pos); + pal = *(resource + pos++); + if (pal >= GFXR_PIC0_NUM_PALETTES) { + GFXERROR("Attempt to write to invalid palette %d\n", pal); + return; + } + + p0printf(" palette[%d] <- (", pal); + for (index = 0; index < GFXR_PIC0_PALETTE_SIZE; index++) { + palette[pal][index] = *(resource + pos++); + if (index > 0) + p0printf(","); + if (!(index & 0x7)) + p0printf("[%d]=", index); + p0printf("%02x", palette[pal][index]); + } + p0printf(")\n"); + goto end_op_loop; + + case PIC_SCI1_OPX_SET_PALETTE: + p0printf("Set palette @%d\n", pos); + pic->visual_map->flags &= ~GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + pic->visual_map->colors = gfxr_read_pal1(resid, &pic->visual_map->colors_nr, + resource+pos, SCI1_PALETTE_SIZE); + pos += SCI1_PALETTE_SIZE; + goto end_op_loop; + + case PIC_SCI0_OPX_MONO0: + p0printf("Monochrome opx 0 @%d\n", pos); + pos += 41; + goto end_op_loop; + + + case PIC_SCI0_OPX_MONO1: + case PIC_SCI0_OPX_MONO3: + ++pos; + p0printf("Monochrome opx %d @%d\n", opx, pos); + goto end_op_loop; + + + case PIC_SCI0_OPX_MONO2: + case PIC_SCI0_OPX_MONO4: /* Monochrome ops: Ignored by us */ + p0printf("Monochrome opx %d @%d\n", opx, pos); + goto end_op_loop; + + + case PIC_SCI0_OPX_EMBEDDED_VIEW: + case PIC_SCI1_OPX_EMBEDDED_VIEW: + { + int posx, posy; + int bytesize; +/* byte *vismap = pic->visual_map->index_data; */ + int nodraw = 0; + + gfx_pixmap_t *view; + gfx_mode_t *mode; + + p0printf("Embedded view @%d\n", pos); + + /* Set up mode structure for resizing the view */ + mode = gfx_new_mode( + pic->visual_map->index_xl/320, + pic->visual_map->index_yl/200, + 1, /* 1bpp, which handles masks and the rest for us */ + 0, 0, 0, 0, 0, 0, 0, 0, 16, 0); + + GET_ABS_COORDS(posx, posy); + bytesize = (*(resource + pos))+(*(resource + pos + 1) << 8); + p0printf("(%d, %d)\n", posx, posy); + pos += 2; + if (!sci1 && !nodraw) + view = gfxr_draw_cel0(-1,-1,-1, resource + pos, bytesize, NULL, 0); + else + view = gfxr_draw_cel1(-1,-1,-1, 0, resource + pos, bytesize, NULL, + static_pal_nr == GFX_SCI1_AMIGA_COLORS_NR); + pos+=bytesize; + if (nodraw) continue; + p0printf("(%d, %d)-(%d, %d)\n", + posx, + posy, + posx + view->index_xl, + posy + view->index_yl); + + /* we can only safely replace the palette if it's static + *if it's not for some reason, we should die + */ + if (!(view->flags & GFX_PIXMAP_FLAG_EXTERNAL_PALETTE) && !sci1) { + sciprintf("gfx_draw_pic0(): can't set a non-static palette for an embedded view!\n"); + } + + /* For SCI0, use special color mapping to copy the low + ** nibble of the color index to the high + ** nibble. + */ + if (sci1) { + if (static_pal_nr == GFX_SCI1_AMIGA_COLORS_NR) { + /* Assume Amiga game */ + pic->visual_map->colors = static_pal; + pic->visual_map->colors_nr = static_pal_nr; + pic->visual_map->flags |= GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + } + view->colors = pic->visual_map->colors; + view->colors_nr = pic->visual_map->colors_nr; + } else + view->colors = embedded_view_colors; + + /* Hack to prevent overflowing the visual map buffer. + Yes, this does happen otherwise. */ + if (view->index_yl + sci_titlebar_size > 200) + sci_titlebar_size = 0; + + gfx_xlate_pixmap(view, mode, GFX_XLATE_FILTER_NONE); + + if (flags & DRAWPIC01_FLAG_OVERLAID_PIC) + view_transparentize(view, pic->visual_map->index_data, + posx, sci_titlebar_size+posy, + view->index_xl, view->index_yl); + + _gfx_crossblit_simple(pic->visual_map->index_data+(sci_titlebar_size*320)+ + posy*320+posx, + view->index_data, + pic->visual_map->index_xl, view->index_xl, + view->index_xl, + view->index_yl, + 1); + + gfx_free_mode(mode); + gfx_free_pixmap(NULL, view); + } + goto end_op_loop; + + case PIC_SCI0_OPX_SET_PRIORITY_TABLE: + case PIC_SCI1_OPX_PRIORITY_TABLE_EXPLICIT: { + int i; + int *pri_table; + + p0printf("Explicit priority table @%d\n", pos); + if (!pic->internal) + { + pic->internal = sci_malloc(16 * sizeof(int)); + } else + { + GFXERROR("pic->internal is not NULL (%08x); this only occurs with overlaid pics, otherwise it's a bug!\n", pic->internal); + } + + pri_table = (int*)pic->internal; + + pri_table[0] = 0; + pri_table[15] = 190; + + for (i = 1; i < 15; i++) + pri_table[i] = resource[pos++]; + } + goto end_op_loop; + + case PIC_SCI1_OPX_PRIORITY_TABLE_EQDIST: + { + int first = getInt16(resource + pos); + int last = getInt16(resource + pos + 2); + int nr; + int *pri_table; + + if (!pic->internal) + { + pic->internal = sci_malloc(16 * sizeof(int)); + } else + { + GFXERROR("pic->internal is not NULL (%08x); possible memory corruption!\n", pic->internal); + } + + pri_table = (int*)pic->internal; + + for (nr = 0; nr < 16; nr ++) + pri_table[nr] = SCI0_PRIORITY_BAND_FIRST_14_ZONES(nr); + pos += 4; + goto end_op_loop; + } + + default: sciprintf("%s L%d: Warning: Unknown opx %02x\n", __FILE__, __LINE__, opx); + return; + } + goto end_op_loop; + + case PIC_OP_TERMINATE: + p0printf("Terminator\n"); + /* WARNING( "ARTIFACT REMOVAL CODE is commented out!") */ + /* _gfxr_vismap_remove_artifacts(); */ + return; + + default: GFXWARN("Unknown op %02x\n", op); + return; + } +end_op_loop: + {} + } + + GFXWARN("Reached end of pic resource %04x\n", resid); +} + +void +gfxr_draw_pic11(gfxr_pic_t *pic, int flags, int default_palette, int size, + byte *resource, gfxr_pic0_params_t *style, int resid, + gfx_pixmap_color_t *static_pal, int static_pal_nr) +{ + int has_bitmap = getUInt16(resource + 4); + int vector_data_ptr = getUInt16(resource + 16); + int palette_data_ptr = getUInt16(resource + 28); + int bitmap_data_ptr = getUInt16(resource + 32); + int sci_titlebar_size = style->pic_port_bounds.y; + gfx_mode_t *mode; + gfx_pixmap_t *view = NULL; + /* Set up mode structure for resizing the view */ + mode = gfx_new_mode( + pic->visual_map->index_xl/320, + pic->visual_map->index_yl/200, + 1, /* 1bpp, which handles masks and the rest for us */ + 0, 0, 0, 0, 0, 0, 0, 0, 16, 0); + + pic->visual_map->colors = gfxr_read_pal11(-1, &(pic->visual_map->colors_nr), resource + palette_data_ptr, 1284); + + if (has_bitmap) + view = gfxr_draw_cel11(-1, 0, 0, 0, resource, resource + bitmap_data_ptr, size - bitmap_data_ptr, NULL); + + if (view) + { + view->colors = pic->visual_map->colors; + view->colors_nr = pic->visual_map->colors_nr; + + gfx_xlate_pixmap(view, mode, GFX_XLATE_FILTER_NONE); + + if (flags & DRAWPIC01_FLAG_OVERLAID_PIC) + view_transparentize(view, pic->visual_map->index_data, + 0, 0, + view->index_xl, view->index_yl); + + /* Hack to prevent overflowing the visual map buffer. + Yes, this does happen otherwise. */ + if (view->index_yl + sci_titlebar_size > 200) + sci_titlebar_size = 0; + + _gfx_crossblit_simple(pic->visual_map->index_data+sci_titlebar_size*view->index_xl, + view->index_data, + pic->visual_map->index_xl, view->index_xl, + view->index_xl, + view->index_yl, + 1); + } else + { + GFXWARN("No view was contained in SCI1.1 pic resource"); + } + + + + gfxr_draw_pic01(pic, flags, default_palette, size - vector_data_ptr, + resource + vector_data_ptr, style, resid, 1, + static_pal, static_pal_nr); +} + +void +gfxr_dither_pic0(gfxr_pic_t *pic, int dmode, int pattern) +{ + int xl = pic->visual_map->index_xl; + int yl = pic->visual_map->index_yl; + int xfrob_max = (pattern == GFXR_DITHER_PATTERN_1)? 1 : pic->mode->xfact; + int yfrob_max = (pattern == GFXR_DITHER_PATTERN_1)? 1 : pic->mode->yfact; + int xfrobc = 0, yfrobc = 0; + int selection = 0; + int x, y; + byte *data = pic->visual_map->index_data; + + if (dmode == GFXR_DITHER_MODE_F256) + return; /* Nothing to do */ + + if (dmode == GFXR_DITHER_MODE_D16) { /* Limit to 16 colors */ + pic->visual_map->colors = gfx_sci0_image_colors[sci0_palette]; + pic->visual_map->colors_nr = GFX_SCI0_IMAGE_COLORS_NR; + } + + for (y = 0; y < yl; y++) { + for (x = 0; x < xl; x++) { + + switch (dmode) { + + case GFXR_DITHER_MODE_D16: + if (selection) + *data = (*data & 0xf0) >> 4; + else + *data = (*data & 0xf); + break; + + case GFXR_DITHER_MODE_D256: + if (selection) + *data = ((*data & 0xf) << 4) | ((*data & 0xf0) >> 4); + break; + + default: + GFXERROR("Invalid dither mode %d!\n", dmode); + return; + } + + ++data; + + if (++xfrobc == xfrob_max) { + selection = !selection; + xfrobc = 0; + } + } + + if (++yfrobc == yfrob_max) { + selection = !selection; + yfrobc = 0; + } + } +} + diff --git a/engines/sci/gfx/resource/sci_picfill.c b/engines/sci/gfx/resource/sci_picfill.c new file mode 100644 index 0000000000..c7ecea08a7 --- /dev/null +++ b/engines/sci/gfx/resource/sci_picfill.c @@ -0,0 +1,429 @@ +/*************************************************************************** + Copyright (C) 2004 Christoph Reichenbach + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +***************************************************************************/ + +/* Generic pic filling code, to be included by sci_pic_0.c + * + * + * To use, define the following: + * FILL_FUNCTION: Name of the exported floodfill function + * FILL_FUNCTION_RECURSIVE: Name of the helper function + * + * Define DRAW_SCALED to support scaled drawing, or leave it out for faster + * processing. + * + */ + +#ifdef FILL_RECURSIVE_DEBUG +# define PRINT_DEBUG0(s) if (!fillmagc) fprintf(stderr, s) +# define PRINT_DEBUG1(s,p1) if (!fillmagc) fprintf(stderr, s, p1) +# define PRINT_DEBUG4(s,p1,p2,p3,p4) if (!fillmagc) fprintf(stderr, s, p1) +#else +# define PRINT_DEBUG0(s) +# define PRINT_DEBUG1(s,p1) +# define PRINT_DEBUG4(s,p1,p2,p3,p4) +#endif + +#ifdef DRAW_SCALED +# define SCALED_CHECK(x) (x) +# define IS_BOUNDARY(x, y, index) (((index) & legalmask) != legalcolor) +#else +# define SCALED_CHECK(x) 1 +# define IS_BOUNDARY(x, y, index) ( \ + (((x)+(y)) & 1)? /* figure out which part of the mask to use, to simulate dithering */ \ + ((((index)) & ((legalmask) )) != ((legalcolor) & ((legalmask)))) /* odd coordinate */ \ + : ((((index)) & ((legalmask) >> 8)) != ((legalcolor) & ((legalmask) >> 8))) /* even coordinate */ \ + ) +#endif + +static void +FILL_FUNCTION_RECURSIVE(gfxr_pic_t *pic, int old_xl, int old_xr, int y, int dy, byte *bounds, + int legalcolor, int legalmask, int color, int priority, int drawenable, + int sci_titlebar_size) +{ + int linewidth = pic->mode->xfact * 320; + int miny = pic->mode->yfact * sci_titlebar_size; + int maxy = pic->mode->yfact * 200; + int xl, xr; + int oldytotal = y * linewidth; +#ifdef DRAW_SCALED + int old_proj_y = -42; + int proj_y; + int proj_ytotal; + int proj_x; + int proj_xl_bound = 0; + int proj_xr_bound = 0; +#endif + + do { + int ytotal = oldytotal + (linewidth * dy); + int xcont; + int state; + + y += dy; + +#ifdef FILL_RECURSIVE_DEBUG + if (!fillc) + return; + else if (!fillmagc) { --fillc; + } +#endif /* defined(FILL_RECURSIVE_DEBUG) */ + + if (y < miny || y >= maxy) { + PRINT_DEBUG0("ABRT on failed initial assertion!\n"); + return; + } +#ifdef DRAW_SCALED + proj_y = y / pic->mode->yfact; + + if (proj_y != old_proj_y) { + /* First, find the projected coordinates, unless known already: */ + proj_ytotal = proj_y * 320; + proj_x = old_xl / pic->mode->xfact; + + proj_xl_bound = proj_x; + if (SCALED_CHECK(pic->aux_map[proj_ytotal + proj_xl_bound] & FRESH_PAINT)){ + while (proj_xl_bound + && pic->aux_map[proj_ytotal + proj_xl_bound - 1] & FRESH_PAINT) + --proj_xl_bound; + } else { + while (proj_xl_bound < 319 + && !(pic->aux_map[proj_ytotal + proj_xl_bound + 1] & FRESH_PAINT)) + ++proj_xl_bound; + + if (proj_xl_bound < 319) + ++proj_xl_bound; + } + + if (proj_xl_bound == 319 + && !(pic->aux_map[proj_ytotal + proj_xl_bound] & FRESH_PAINT)) { + PRINT_DEBUG0("ABRT because proj_xl_bound couldn't be found\n"); + return; + } + + proj_xr_bound = (proj_xl_bound > proj_x)? proj_xl_bound : proj_x; + while ((proj_xr_bound < 319) + && pic->aux_map[proj_ytotal + proj_xr_bound + 1] & FRESH_PAINT) + ++proj_xr_bound; + +#ifdef FILL_RECURSIVE_DEBUG + if (!fillmagc) { + fprintf(stderr,"l%d: {%d,%d} | ", proj_y, proj_xl_bound, proj_xr_bound); + pic->aux_map[proj_y*320 + proj_xl_bound] |= 0x2; + pic->aux_map[proj_y*320 + proj_xr_bound] |= 0x2; + } +#endif + + proj_xl_bound *= pic->mode->xfact; + if (proj_xl_bound) + proj_xl_bound -= pic->mode->xfact - 1; + + if (proj_xr_bound < 319) + ++proj_xr_bound; + proj_xr_bound *= pic->mode->xfact; + proj_xr_bound += pic->mode->xfact -1; + + old_proj_y = proj_y; + } +#else +# define proj_xl_bound 0 +# define proj_xr_bound 319 +#endif + + /* Now we have the projected limits, get the real ones: */ + + xl = (old_xl > proj_xl_bound)? old_xl : proj_xl_bound; + if (!IS_BOUNDARY(xl, y+1, bounds[ytotal + xl])) { /* go left as far as possible */ + while (xl > proj_xl_bound && (!IS_BOUNDARY(xl-1, y+1, bounds[ytotal + xl - 1]))) + --xl; + } else /* go right until the fillable area starts */ + while (xl < proj_xr_bound && (IS_BOUNDARY(xl, y+1, bounds[ytotal + xl]))) + ++xl; + + + PRINT_DEBUG1("<%d,", xl); + + if ((xl > proj_xr_bound) + || (xl > old_xr)) { + PRINT_DEBUG0("ABRT because xl > xr_bound\n"); + return; + } + + xr = (xl > old_xl)? xl : old_xl; + while (xr < proj_xr_bound && (!IS_BOUNDARY(xr+1, y+1, bounds[ytotal + xr + 1]))) + ++xr; + + PRINT_DEBUG1("%d> -> ", xr); + + if (IS_BOUNDARY(xl, y+1, bounds[ytotal + xl])) { + PRINT_DEBUG0("ABRT because xl illegal\n"); + return; + } + +#ifdef DRAW_SCALED + PRINT_DEBUG4("[%d[%d,%d]%d]\n", proj_xl_bound, xl, xr, proj_xr_bound); + + if (xl < proj_xl_bound && xr - 3*pic->mode->xfact < proj_xl_bound) { + PRINT_DEBUG0("ABRT interval left of zone\n"); + return; + } + + if (xr > proj_xr_bound && xl + 3*pic->mode->xfact > proj_xr_bound) { + PRINT_DEBUG0("ABRT because interval right of zone\n"); + return; + } +#endif + + if (drawenable & GFX_MASK_VISUAL) + memset(pic->visual_map->index_data + ytotal + xl, color, xr - xl + 1); + + if (drawenable & GFX_MASK_PRIORITY) + memset(pic->priority_map->index_data + ytotal + xl, priority, xr - xl + 1); + + + /* Check whether we need to recurse on branches in the same direction */ + state = 0; + xcont = xr + 1; + while (xcont <= old_xr) { + if (IS_BOUNDARY(xcont, y+1, bounds[ytotal + xcont])) + state = xcont; + else if (state) { /* recurse */ + PRINT_DEBUG4("[%d[%d,%d],%d]: ", old_xl, xl, xr, old_xr); + PRINT_DEBUG4("rec BRANCH %d [%d,%d] l%d\n", dy, state, xcont, y - dy); + + FILL_FUNCTION_RECURSIVE(pic, state, xcont, y - dy, dy, bounds, legalcolor, + legalmask, color, priority, drawenable, sci_titlebar_size); + state = 0; + } + ++xcont; + } + + /* Check whether we need to recurse on backward branches: */ + /* left */ + if (xl < old_xl - 1) { + state = 0; + for (xcont = old_xl-1; xcont >= xl; xcont--) { + if (IS_BOUNDARY(xcont, y, bounds[oldytotal + xcont])) + state = xcont; + else if (state) { /* recurse */ + PRINT_DEBUG4("[%d[%d,%d],%d]: ", old_xl, xl, xr, old_xr); + PRINT_DEBUG4("rec BACK-LEFT %d [%d,%d] l%d\n", -dy, state, xcont, y); + + FILL_FUNCTION_RECURSIVE(pic, xcont, state, y, -dy, bounds, + legalcolor, legalmask, color, priority, drawenable, + sci_titlebar_size); + state = 0; + } + } + } + + /* right */ + if (xr > old_xr + 1) { + state = 0; + for (xcont = old_xr + 1; xcont <= xr; xcont++) { + if (IS_BOUNDARY(xcont, y, bounds[oldytotal + xcont])) + state = xcont; + else if (state) { /* recurse */ + PRINT_DEBUG4("[%d[%d,%d],%d]: ", old_xl, xl, xr, old_xr); + PRINT_DEBUG4("rec BACK-RIGHT %d [%d,%d] l%d\n", -dy, state, xcont, y); + + FILL_FUNCTION_RECURSIVE(pic, state, xcont, y, -dy, bounds, + legalcolor, legalmask, color, priority, drawenable, + sci_titlebar_size); + state = 0; + } + } + } + + oldytotal = ytotal; + old_xl = xl; + old_xr = xr; + + } while (1); +} + + +static void +FILL_FUNCTION(gfxr_pic_t *pic, int x_320, int y_200, int color, int priority, int control, int drawenable, + int sci_titlebar_size) +{ + int linewidth = pic->mode->xfact * 320; + int x, y; + int xl, xr; + int ytotal; + int bitmask; + byte *bounds = NULL; + int legalcolor, legalmask; +#ifdef DRAW_SCALED + int min_x, min_y, max_x, max_y; +#endif + int original_drawenable = drawenable; /* Backup, since we need the unmodified value + ** for filling the aux and control map */ + + /* Restrict drawenable not to restrict itself to zero */ + if (pic->control_map->index_data[y_200 * 320 + x_320] != 0) + drawenable &= ~GFX_MASK_CONTROL; + + if (color == 0xff) + drawenable &= ~GFX_MASK_VISUAL; + + if (priority == 0) { + drawenable &= ~GFX_MASK_PRIORITY; + original_drawenable &= ~GFX_MASK_PRIORITY; + } + + AUXBUF_FILL(pic, x_320, y_200, original_drawenable, + (drawenable & GFX_MASK_CONTROL)? control : 0, + sci_titlebar_size); + +#ifdef DRAW_SCALED + _gfxr_auxbuf_spread(pic, &min_x, &min_y, &max_x, &max_y); + + if (_gfxr_find_fill_point(pic, min_x, min_y, max_x, max_y, x_320, y_200, color, drawenable, &x, &y)) { + /* GFXWARN("Could not find scaled fill point, but unscaled fill point was available!\n"); */ + drawenable &= GFX_MASK_PRIORITY; + if (!drawenable) + _gfxr_auxbuf_propagate_changes(pic, 0); + } +#else + x = x_320; + y = y_200; +#endif + + ytotal = y * linewidth; + + if (!drawenable) + return; + + if (drawenable & GFX_MASK_VISUAL) { + bounds = pic->visual_map->index_data; +#if 0 + /* Code disabled, as removing it fixes qg1 pic.095 (unscaled). However, + ** it MAY be of relevance to scaled pic drawing... */ + + if ((color & 0xf) == 0xf /* When dithering with white, do more + ** conservative checks */ + || (color & 0xf0) == 0xf0) + legalcolor = 0xff; + else + legalcolor = 0xf0; /* Only check the second color */ +#endif +#ifdef DRAW_SCALED + legalcolor = 0xff; + legalmask = legalcolor; +#else + legalmask = 0x0ff0; + legalcolor = 0xff; +#endif + } else if (drawenable & GFX_MASK_PRIORITY) { + bounds = pic->priority_map->index_data; + legalcolor = 0; + legalmask = 0x0f0f; + } else { + legalcolor = 0; + legalmask = 0x0f0f; + } + + if (!bounds || IS_BOUNDARY(x, y, bounds[ytotal + x])) + return; + + if (bounds) { +#ifdef DRAW_SCALED + int proj_y = y_200; + int proj_ytotal = proj_y * 320; + int proj_x = x_320; + int proj_xl_bound; + int proj_xr_bound; + int proj_xl, proj_xr; + + ytotal = y * linewidth; + + proj_xl_bound = proj_x; + if (SCALED_CHECK(pic->aux_map[proj_ytotal + proj_xl_bound] & FRESH_PAINT)) { + while (proj_xl_bound + && SCALED_CHECK(pic->aux_map[proj_ytotal + proj_xl_bound - 1] & FRESH_PAINT)) + --proj_xl_bound; + } else + while (proj_xl_bound < 319 + && SCALED_CHECK(!(pic->aux_map[proj_ytotal + proj_xl_bound + 1] & FRESH_PAINT))) + ++proj_xl_bound; + + proj_xr_bound = (proj_xl_bound > proj_x)? proj_xl_bound : proj_x; + while ((proj_xr_bound < 319) && + SCALED_CHECK(pic->aux_map[proj_ytotal + proj_xr_bound + 1] & FRESH_PAINT)) + ++proj_xr_bound; + + proj_xl = proj_xl_bound; + proj_xr = proj_xr_bound; + + proj_xl_bound *= pic->mode->xfact; + if (proj_xl_bound) + proj_xl_bound -= pic->mode->xfact -1; + + if (proj_xr_bound < 319) + ++proj_xr_bound; + proj_xr_bound *= pic->mode->xfact; + proj_xr_bound += pic->mode->xfact -1; +#endif + xl = x; + while (xl > proj_xl_bound && (!IS_BOUNDARY(xl-1, y, bounds[ytotal + xl -1]))) + --xl; + + while (x < proj_xr_bound && (!IS_BOUNDARY(x+1, y, bounds[ytotal + x + 1]))) + ++x; + xr = x; + + if (drawenable & GFX_MASK_VISUAL) + memset(pic->visual_map->index_data + ytotal + xl, color, xr - xl + 1); + + if (drawenable & GFX_MASK_PRIORITY) + memset(pic->priority_map->index_data + ytotal + xl, priority, xr - xl + 1); + + FILL_FUNCTION_RECURSIVE(pic, xl, xr, y, -1, bounds, legalcolor, legalmask, color, priority, drawenable, + sci_titlebar_size); + FILL_FUNCTION_RECURSIVE(pic, xl, xr, y, +1, bounds, legalcolor, legalmask, color, priority, drawenable, + sci_titlebar_size); + } + + /* Now finish the aux buffer */ + bitmask = drawenable & + ( + ((color != 0xff)? 1 : 0) + | ((priority)? 2 : 0) + | ((control)? 4 : 0) + ); + +#ifdef DRAW_SCALED +# ifdef FILL_RECURSIVE_DEBUG + if (fillmagc) +# endif + _gfxr_auxbuf_propagate_changes(pic, bitmask); +#endif +} + +#undef SCALED_CHECK +#undef IS_BOUNDARY + +#ifndef DRAW_SCALED +# undef proj_xl_bound +# undef proj_xr_bound +#endif + + + diff --git a/engines/sci/gfx/resource/sci_picfill_aux.c b/engines/sci/gfx/resource/sci_picfill_aux.c new file mode 100644 index 0000000000..70d8a822da --- /dev/null +++ b/engines/sci/gfx/resource/sci_picfill_aux.c @@ -0,0 +1,205 @@ +/*************************************************************************** + Copyright (C) 2004 Christoph Reichenbach + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +***************************************************************************/ + +/* Generic pic auxbuf filling code, to be included by sci_pic_0.c + * + * + * To use, define the following: + * AUXBUF_FILL: Name of the exported floodfill function + * AUXBUF_FILL_HELPER: Name of the helper function + * + * Define DRAW_SCALED to support scaled drawing, or leave it out for faster + * processing. + * + */ + +#define CLIPMASK_HARD_BOUND 0x80 /* ensures that we don't re-fill filled stuff */ + +static void +AUXBUF_FILL_HELPER(gfxr_pic_t *pic, int old_xl, int old_xr, int y, int dy, + int clipmask, int control, int sci_titlebar_size) +{ + int xl, xr; + int oldytotal = y * 320; +#ifdef DRAW_SCALED + unsigned const char fillmask = CLIPMASK_HARD_BOUND | 0x78; +#else + unsigned const char fillmask = CLIPMASK_HARD_BOUND | 0x84; +#endif + + do { + int ytotal = oldytotal + (320 * dy); + int xcont; + int state; + + y += dy; + + if (y < sci_titlebar_size || y > 199) + return; + + xl = old_xl; + if (!(pic->aux_map[ytotal + xl] & clipmask)) { /* go left */ + while (xl && !(pic->aux_map[ytotal + xl - 1] & clipmask)) + --xl; + } else /* go right and look for the first valid spot */ + while ((xl <= old_xr) && (pic->aux_map[ytotal + xl] & clipmask)) + ++xl; + + if (xl > old_xr) /* No fillable strip above the last one */ + return; + + if ((ytotal + xl) < 0) { fprintf(stderr,"AARGH-%d\n", __LINE__); BREAKPOINT(); } + + xr = xl; + while (xr < 320 && !(pic->aux_map[ytotal + xr] & clipmask)) { + pic->aux_map[ytotal + xr] |= fillmask; + ++xr; + } + + if ((ytotal + xr) > 64000) { fprintf(stderr,"AARGH-%d\n", __LINE__); + BREAKPOINT(); + } + + --xr; + + if (xr < xl) + return; + + /* Check whether we need to recurse on branches in the same direction */ + if ((y > sci_titlebar_size && dy < 0) + || (y < 199 && dy > 0)) { + + state = 0; + xcont = xr + 1; + while (xcont <= old_xr) { + if (pic->aux_map[ytotal + xcont] & clipmask) + state = 0; + else if (!state) { /* recurse */ + state = 1; + AUXBUF_FILL_HELPER(pic, xcont, old_xr, + y - dy, dy, clipmask, control, sci_titlebar_size); + } + ++xcont; + } + } + + /* Check whether we need to recurse on backward branches: */ + /* left */ + if (xl < old_xl - 1) { + state = 0; + for (xcont = old_xl - 1; xcont >= xl; xcont--) { + if (pic->aux_map[oldytotal + xcont] & clipmask) + state = xcont; + else if (state) { /* recurse */ + AUXBUF_FILL_HELPER(pic, xcont, state, + y, -dy, clipmask, control, sci_titlebar_size); + state = 0; + } + } + } + + /* right */ + if (xr > old_xr + 1) { + state = 0; + for (xcont = old_xr + 1; xcont <= xr; xcont++) { + if (pic->aux_map[oldytotal + xcont] & clipmask) + state = xcont; + else if (state) { /* recurse */ + AUXBUF_FILL_HELPER(pic, state, xcont, + y, -dy, clipmask, control, sci_titlebar_size); + state = 0; + } + } + } + + if ((ytotal + xl) < 0) { fprintf(stderr,"AARGH-%d\n", __LINE__); BREAKPOINT() } + if ((ytotal + xr+1) > 64000) { fprintf(stderr,"AARGH-%d\n", __LINE__); BREAKPOINT(); } + + if (control) + memset(pic->control_map->index_data + ytotal + xl, control, xr-xl+1); + + oldytotal = ytotal; + old_xr = xr; + old_xl = xl; + + } while (1); +} + + +static void +AUXBUF_FILL(gfxr_pic_t *pic, int x, int y, int clipmask, int control, int sci_titlebar_size) +{ + /* Fills the aux buffer and the control map (if control != 0) */ + int xl, xr; + int ytotal = y * 320; +#ifdef DRAW_SCALED + unsigned const char fillmask = 0x78; +#else + unsigned const char fillmask = 0x4; +#endif + +#ifndef DRAW_SCALED + if (!control || !(clipmask & 4)) + return; /* Without pic scaling, we only do this to fill the control map */ +#endif + + if (clipmask & 1) + clipmask = 1; /* vis */ + else if (clipmask & 2) + clipmask = 2; /* pri */ + else if (clipmask & 4) + clipmask = 4; /* ctl */ + else return; + +#ifdef DRAW_SCALED + clipmask |= fillmask; /* Bits 3-5 */ +#endif + + if (pic->aux_map[ytotal + x] & clipmask) + return; + + pic->aux_map[ytotal + x] |= fillmask; + + xl = x; + while (xl && !(pic->aux_map[ytotal + xl - 1] & clipmask)) { + --xl; + pic->aux_map[ytotal + xl] |= fillmask; + } + + xr = x; + while ((xr < 319) && !(pic->aux_map[ytotal + xr + 1] & clipmask)) { + ++xr; + pic->aux_map[ytotal + xr] |= fillmask; + } + + clipmask |= CLIPMASK_HARD_BOUND; /* Guarantee clipping */ + + if (control) /* Draw the same strip on the control map */ + memset(pic->control_map->index_data + ytotal + xl, control, xr - xl + 1); + + if (y > sci_titlebar_size) + AUXBUF_FILL_HELPER(pic, xl, xr, y, -1, clipmask, control, sci_titlebar_size); + + if (y < 199) + AUXBUF_FILL_HELPER(pic, xl, xr, y, +1, clipmask, control, sci_titlebar_size); +} + + +#undef CLIPMASK_HARD_BOUND diff --git a/engines/sci/gfx/resource/sci_resmgr.c b/engines/sci/gfx/resource/sci_resmgr.c new file mode 100644 index 0000000000..6475369230 --- /dev/null +++ b/engines/sci/gfx/resource/sci_resmgr.c @@ -0,0 +1,341 @@ +/*************************************************************************** + sci_resmgr.c Copyright (C) 2000 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* The interpreter-specific part of the resource manager, for SCI */ + +#include +#include +#include +#include +#include + +int +gfxr_interpreter_options_hash(gfx_resource_type_t type, int version, + gfx_options_t *options, + void *internal, int palette) +{ + switch (type) { + + case GFX_RESOURCE_TYPE_VIEW: + return palette; + + case GFX_RESOURCE_TYPE_PIC: + if (version >= SCI_VERSION_01_VGA) + return options->pic_port_bounds.y; + else + return (options->pic0_unscaled)? 0x10000 : + (options->pic0_dither_mode << 12) + | (options->pic0_dither_pattern << 8) + | (options->pic0_brush_mode << 4) + | (options->pic0_line_mode); + + case GFX_RESOURCE_TYPE_FONT: + return 0; + + case GFX_RESOURCE_TYPE_CURSOR: + return 0; + + case GFX_RESOURCE_TYPES_NR: + default: + GFXERROR("Invalid resource type: %d\n", type); + return -1; + } +} + + +gfxr_pic_t * +gfxr_interpreter_init_pic(int version, gfx_mode_t *mode, int ID, void *internal) +{ + return gfxr_init_pic(mode, ID, version >= SCI_VERSION_01_VGA); +} + + +void +gfxr_interpreter_clear_pic(int version, gfxr_pic_t *pic, void *internal) +{ + gfxr_clear_pic0(pic, SCI_TITLEBAR_SIZE); +} + + +int +gfxr_interpreter_calculate_pic(gfx_resstate_t *state, gfxr_pic_t *scaled_pic, gfxr_pic_t *unscaled_pic, + int flags, int default_palette, int nr, void *internal) +{ + resource_mgr_t *resmgr = (resource_mgr_t *) state->misc_payload; + resource_t *res = scir_find_resource(resmgr, sci_pic, nr, 0); + int need_unscaled = unscaled_pic != NULL; + gfxr_pic0_params_t style, basic_style; + + basic_style.line_mode = GFX_LINE_MODE_CORRECT; + basic_style.brush_mode = GFX_BRUSH_MODE_SCALED; + basic_style.pic_port_bounds = state->options->pic_port_bounds; + + style.line_mode = state->options->pic0_line_mode; + style.brush_mode = state->options->pic0_brush_mode; + style.pic_port_bounds = state->options->pic_port_bounds; + + if (!res || !res->data) + return GFX_ERROR; + + if (state->version >= SCI_VERSION_01_VGA) { + if (need_unscaled) + { + if (state->version == SCI_VERSION_1_1) + gfxr_draw_pic11(unscaled_pic, flags, default_palette, res->size, res->data, &basic_style, res->id, + state->static_palette, state->static_palette_entries); + else + gfxr_draw_pic01(unscaled_pic, flags, default_palette, res->size, res->data, &basic_style, res->id, 1, + state->static_palette, state->static_palette_entries); + } + if (scaled_pic && scaled_pic->undithered_buffer) + memcpy(scaled_pic->visual_map->index_data, scaled_pic->undithered_buffer, scaled_pic->undithered_buffer_size); + + if (state->version == SCI_VERSION_1_1) + gfxr_draw_pic11(scaled_pic, flags, default_palette, res->size, res->data, &style, res->id, + state->static_palette, state->static_palette_entries); + else + gfxr_draw_pic01(scaled_pic, flags, default_palette, res->size, res->data, &style, res->id, state->version, + state->static_palette, state->static_palette_entries); + } else { + if (need_unscaled) + gfxr_draw_pic01(unscaled_pic, flags, default_palette, res->size, res->data, &basic_style, res->id, 0, + state->static_palette, state->static_palette_entries); + + if (scaled_pic && scaled_pic->undithered_buffer) + memcpy(scaled_pic->visual_map->index_data, scaled_pic->undithered_buffer, scaled_pic->undithered_buffer_size); + + gfxr_draw_pic01(scaled_pic, flags, default_palette, res->size, res->data, &style, res->id, 0, + state->static_palette, state->static_palette_entries); + if (need_unscaled) + gfxr_remove_artifacts_pic0(scaled_pic, unscaled_pic); + + if (!scaled_pic->undithered_buffer) + scaled_pic->undithered_buffer = sci_malloc(scaled_pic->undithered_buffer_size); + + memcpy(scaled_pic->undithered_buffer, scaled_pic->visual_map->index_data, scaled_pic->undithered_buffer_size); + + gfxr_dither_pic0(scaled_pic, state->options->pic0_dither_mode, state->options->pic0_dither_pattern); + } + + /* Mark default palettes */ + if (scaled_pic) + scaled_pic->visual_map->loop = default_palette; + + if (unscaled_pic) + unscaled_pic->visual_map->loop = default_palette; + + return GFX_OK; +} + + +void +gfxr_palettize_view(gfxr_view_t *view, gfx_pixmap_color_t *source, int source_entries) +{ + int i; + + for (i=0;icolors_nr,source_entries);i++) + { + if ((view->colors[i].r == 0) && + (view->colors[i].g == 0) && + (view->colors[i].b == 0)) + { + view->colors[i] = source[i]; + } + } +} + +gfxr_view_t * +gfxr_draw_view11(int id, byte *resource, int size); + +gfxr_view_t * +gfxr_interpreter_get_view(gfx_resstate_t *state, int nr, void *internal, int palette) +{ + resource_mgr_t *resmgr = (resource_mgr_t *) state->misc_payload; + resource_t *res = scir_find_resource(resmgr, sci_view, nr, 0); + int resid = GFXR_RES_ID(GFX_RESOURCE_TYPE_VIEW, nr); + gfxr_view_t *result; + + if (!res || !res->data) + return NULL; + + if (state->version < SCI_VERSION_01) palette=-1; + + switch (state->version) + { + case SCI_VERSION_0: + case SCI_VERSION_01: + result=gfxr_draw_view0(resid, res->data, res->size, palette); + break; + case SCI_VERSION_01_VGA: + case SCI_VERSION_01_VGA_ODD: + case SCI_VERSION_1_EARLY: + case SCI_VERSION_1_LATE: + result=gfxr_draw_view1(resid, res->data, res->size, state->static_palette, state->static_palette_entries); + break; + case SCI_VERSION_1_1: + case SCI_VERSION_32: + result=gfxr_draw_view11(resid, res->data, res->size); + break; + } + + if (state->version >= SCI_VERSION_01_VGA) + { + if (!result->colors) + { + result->colors = (gfx_pixmap_color_t*)sci_malloc(sizeof(gfx_pixmap_color_t) * state->static_palette_entries); + memset(result->colors, 0, sizeof(gfx_pixmap_color_t) * state->static_palette_entries); + result->colors_nr = state->static_palette_entries; + } + gfxr_palettize_view(result, state->static_palette, state->static_palette_entries); + } + return result; +} + + +gfx_bitmap_font_t * +gfxr_interpreter_get_font(gfx_resstate_t *state, int nr, void *internal) +{ + resource_mgr_t *resmgr = (resource_mgr_t *) state->misc_payload; + resource_t *res = scir_find_resource(resmgr, sci_font, nr, 0); + if (!res || !res->data) + return NULL; + + return gfxr_read_font(res->id, res->data, res->size); +} + + +gfx_pixmap_t * +gfxr_interpreter_get_cursor(gfx_resstate_t *state, int nr, void *internal) +{ + resource_mgr_t *resmgr = (resource_mgr_t *) state->misc_payload; + resource_t *res = scir_find_resource(resmgr, sci_cursor, nr, 0); + int resid = GFXR_RES_ID(GFX_RESOURCE_TYPE_CURSOR, nr); + + if (!res || !res->data) + return NULL; + + if (state->version >= SCI_VERSION_1_1) { + GFXWARN("Attempt to retreive cursor in SCI1.1 or later\n"); + return NULL; + } + + if (state->version == SCI_VERSION_0) + return gfxr_draw_cursor0(resid, res->data, res->size); + else + return gfxr_draw_cursor01(resid, res->data, res->size); +} + + +int * +gfxr_interpreter_get_resources(gfx_resstate_t *state, gfx_resource_type_t type, + int version, int *entries_nr, void *internal) +{ + resource_mgr_t *resmgr = (resource_mgr_t *) state->misc_payload; + int restype; + int *resources; + int count = 0; + int top = sci_max_resource_nr[version] + 1; + int i; + switch (type) { + + case GFX_RESOURCE_TYPE_VIEW: restype = sci_view; + break; + + case GFX_RESOURCE_TYPE_PIC: restype = sci_pic; + break; + + case GFX_RESOURCE_TYPE_CURSOR: restype = sci_cursor; + break; + + case GFX_RESOURCE_TYPE_FONT: restype = sci_font; + break; + + default: + GFX_DEBUG("Unsupported resource %d\n", type); + return NULL; /* unsupported resource */ + + } + + resources = (int*)sci_malloc(sizeof(int) * top); + + for (i = 0; i < top; i++) + if (scir_test_resource(resmgr, restype, i)) + resources[count++] = i; + + *entries_nr = count; + + return resources; +} + +gfx_pixmap_color_t * +gfxr_interpreter_get_static_palette(gfx_resstate_t *state, int version, int *colors_nr, void *internal) +{ + if (version >= SCI_VERSION_01_VGA) + return gfxr_interpreter_get_palette(state, version, colors_nr, internal, 999); + + *colors_nr = GFX_SCI0_PIC_COLORS_NR; + return gfx_sci0_pic_colors; +} + +gfx_pixmap_color_t * +gfxr_interpreter_get_palette(gfx_resstate_t *state, int version, int *colors_nr, + void *internal, int nr) +{ + resource_mgr_t *resmgr = (resource_mgr_t *) state->misc_payload; + resource_t *res; + + if (version < SCI_VERSION_01_VGA) + return NULL; + + res = scir_find_resource(resmgr, sci_palette, nr, 0); + if (!res || !res->data) + return NULL; + + switch (version) + { + case SCI_VERSION_01_VGA : + case SCI_VERSION_01_VGA_ODD : + case SCI_VERSION_1_EARLY : + case SCI_VERSION_1_LATE : + return gfxr_read_pal1(res->id, colors_nr, res->data, res->size); + case SCI_VERSION_1_1 : + case SCI_VERSION_32 : + GFX_DEBUG("Palettes are not yet supported in this SCI version\n"); + return NULL; + + default: + BREAKPOINT(); + return NULL; + } +} + +int +gfxr_interpreter_needs_multicolored_pointers(int version, void *internal) +{ + return (version > SCI_VERSION_1); +} + + + diff --git a/engines/sci/gfx/resource/sci_view_0.c b/engines/sci/gfx/resource/sci_view_0.c new file mode 100644 index 0000000000..98b3654b3c --- /dev/null +++ b/engines/sci/gfx/resource/sci_view_0.c @@ -0,0 +1,254 @@ +/*************************************************************************** + view_0.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +/* set optimisations for Win32: */ +#ifdef _WIN32 +# include +//# pragma intrinsic( memcpy, memset ) +#endif + +#include +#include +#include +#include + + +gfx_pixmap_t * +gfxr_draw_cel0(int id, int loop, int cel, byte *resource, int size, gfxr_view_t *view, int mirrored) +{ + int xl = get_int_16(resource); + int yl = get_int_16(resource + 2); + int xhot = ((signed char *) resource)[4]; + int yhot = ((signed char *) resource)[5]; + int color_key = resource[6]; + int pos = 7; + int writepos = mirrored? xl : 0; + int pixmap_size = xl * yl; + int line_base = 0; + gfx_pixmap_t *retval = gfx_pixmap_alloc_index_data(gfx_new_pixmap(xl, yl, id, loop, cel)); + byte *dest = retval->index_data; + + retval->color_key = 255; /* Pick something larger than 15 */ + + retval->xoffset = mirrored? xhot : -xhot; + retval->yoffset = -yhot; + + if (view) { + retval->colors = view->colors; + retval->colors_nr = view->colors_nr; + retval->flags |= GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + } else + { + retval->colors = gfx_sci0_image_colors[sci0_palette]; + retval->colors_nr = GFX_SCI0_IMAGE_COLORS_NR; + retval->flags |= GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + } + + if (xl <= 0 || yl <= 0) { + gfx_free_pixmap(NULL, retval); + GFXERROR("View %02x:(%d/%d) has invalid xl=%d or yl=%d\n", id, loop, cel, xl, yl); + return NULL; + } + + if (mirrored) { + + while (yl && pos < size) { + int op = resource[pos++]; + int count = op >> 4; + int color = op & 0xf; + + if (view->flags & GFX_PIXMAP_FLAG_PALETTIZED) + color = view->translation[color]; + + if (color == color_key) + color = retval->color_key; + + while (count) { + int pixels = writepos - line_base; + + if (pixels > count) + pixels = count; + + writepos -= pixels; + memset(dest + writepos, color, pixels); + count -= pixels; + + if (writepos == line_base) { + yl--; + writepos += (xl << 1); + line_base += xl; + } + } + } + } else { + + while (writepos < pixmap_size && pos < size) { + int op = resource[pos++]; + int count = op >> 4; + int color = op & 0xf; + + if (view && (view->flags & GFX_PIXMAP_FLAG_PALETTIZED)) + color = view->translation[color]; + + if (color == color_key) + color = retval->color_key; + + if (writepos + count > pixmap_size) { + GFXERROR("View %02x:(%d/%d) writes RLE data over its designated end at rel. offset 0x%04x\n", id, loop, cel, pos); + return NULL; + } + + memset(dest + writepos, color, count); + writepos += count; + } + } + + return retval; +} + +static int +gfxr_draw_loop0(gfxr_loop_t *dest, int id, int loop, byte *resource, int offset, int size, gfxr_view_t *view, int mirrored) +{ + int i; + int cels_nr = get_int_16(resource + offset); + + if (get_uint_16(resource + offset + 2)) { + GFXWARN("View %02x:(%d): Gray magic %04x in loop, expected white\n", id, loop, get_uint_16(resource + offset + 2)); + } + + if (cels_nr * 2 + 4 + offset > size) { + GFXERROR("View %02x:(%d): Offset array for %d cels extends beyond resource space\n", id, loop, cels_nr); + dest->cels_nr = 0; /* Mark as "delete no cels" */ + dest->cels = NULL; + return 1; + } + + dest->cels = (gfx_pixmap_t**)sci_malloc(sizeof(gfx_pixmap_t *) * cels_nr); + + for (i = 0; i < cels_nr; i++) { + int cel_offset = get_uint_16(resource + offset + 4 + (i << 1)); + gfx_pixmap_t *cel = NULL; + + if (cel_offset >= size) { + GFXERROR("View %02x:(%d/%d) supposed to be at illegal offset 0x%04x\n", id, loop, i, cel_offset); + cel = NULL; + } else + cel = gfxr_draw_cel0(id, loop, i, resource + cel_offset, size - cel_offset, view, mirrored); + + + if (!cel) { + dest->cels_nr = i; + return 1; + } + + dest->cels[i] = cel; + } + + dest->cels_nr = cels_nr; + + return 0; +} + + +#define V0_LOOPS_NR_OFFSET 0 +#define V0_FIRST_LOOP_OFFSET 8 +#define V0_MIRROR_LIST_OFFSET 2 + +gfxr_view_t * +gfxr_draw_view0(int id, byte *resource, int size, int palette) +{ + int i; + gfxr_view_t *view; + int mirror_bitpos = 1; + int mirror_bytepos = V0_MIRROR_LIST_OFFSET; + int palette_ofs = get_int_16(resource + 6); + + if (size < V0_FIRST_LOOP_OFFSET + 8) { + GFXERROR("Attempt to draw empty view %04x\n", id); + return NULL; + } + + view = (gfxr_view_t*)sci_malloc(sizeof(gfxr_view_t)); + view->ID = id; + + view->loops_nr = resource[V0_LOOPS_NR_OFFSET]; + + /* Set palette */ + view->colors_nr = GFX_SCI0_IMAGE_COLORS_NR; + view->flags = GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + view->colors = gfx_sci0_image_colors[sci0_palette]; + + if ((palette_ofs)&&(palette>=0)) + { + byte *paldata = resource + palette_ofs + (palette * GFX_SCI0_IMAGE_COLORS_NR); + + for (i = 0; i < GFX_SCI0_IMAGE_COLORS_NR; i++) + view->translation[i] = *(paldata++); + + view->flags |= GFX_PIXMAP_FLAG_PALETTIZED; + } + + if (view->loops_nr * 2 + V0_FIRST_LOOP_OFFSET > size) { + GFXERROR("View %04x: Not enough space in resource to accomodate for the claimed %d loops\n", id, view->loops_nr); + free(view); + return NULL; + } + + view->loops = (gfxr_loop_t*)sci_malloc(sizeof (gfxr_loop_t) * ((view->loops_nr)? view->loops_nr : 1)); /* Alloc 1 if no loop */ + + for (i = 0; i < view->loops_nr; i++) { + int error_token = 0; + int loop_offset = get_uint_16(resource + V0_FIRST_LOOP_OFFSET + (i << 1)); + int mirrored = resource[mirror_bytepos] & mirror_bitpos; + + if ((mirror_bitpos <<= 1) == 0x100) { + mirror_bytepos++; + mirror_bitpos = 1; + } + + if (loop_offset >= size) { + GFXERROR("View %04x:(%d) supposed to be at illegal offset 0x%04x\n", id, i, loop_offset); + error_token = 1; + } + + if (error_token || gfxr_draw_loop0(view->loops + i, id, i, resource, loop_offset, size, view, mirrored)) { + /* An error occured */ + view->loops_nr = i; + gfxr_free_view(NULL, view); + return NULL; + } + } + + return view; +} + + + + + + + diff --git a/engines/sci/gfx/resource/sci_view_1.c b/engines/sci/gfx/resource/sci_view_1.c new file mode 100644 index 0000000000..1387696416 --- /dev/null +++ b/engines/sci/gfx/resource/sci_view_1.c @@ -0,0 +1,554 @@ +/*************************************************************************** + view_1.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* SCI 1 view resource defrobnicator */ + +#include +#include +#include +#include + +#define V1_LOOPS_NR_OFFSET 0 +#define V1_MIRROR_MASK 2 +#define V1_PALETTE_OFFSET 6 +#define V1_FIRST_LOOP_OFFSET 8 + +#define V1_RLE 0x80 /* run-length encode? */ +#define V1_RLE_BG 0x40 /* background fill */ + +#define NEXT_RUNLENGTH_BYTE(n) \ + if (literal_pos == runlength_pos) \ + literal_pos += n; \ + runlength_pos += n; + +#define NEXT_LITERAL_BYTE(n) \ + if (literal_pos == runlength_pos) \ + runlength_pos += n; \ + literal_pos += n; + +static int +decompress_sci_view(int id, int loop, int cel, byte *resource, byte *dest, int mirrored, int pixmap_size, int size, + int runlength_pos, int literal_pos, int xl, int yl, int color_key) +{ + int writepos = mirrored? xl : 0; + + if (mirrored) { + int linebase = 0; + + while (linebase < pixmap_size && literal_pos < size && runlength_pos < size) { + int op = resource[runlength_pos]; + int bytes; + int readbytes = 0; + int color; + + NEXT_RUNLENGTH_BYTE(1); + + if (op & V1_RLE) { + bytes = op & 0x3f; + op &= (V1_RLE | V1_RLE_BG); + readbytes = (op & V1_RLE_BG)? 0 : 1; + } else { + readbytes = bytes = op & 0x3f; + op = 0; + } + + if (runlength_pos + readbytes > size) + { + GFXWARN("View %02x:(%d/%d) requires %d bytes to be read when %d are available at pos %d\n", + id, loop, cel, readbytes, size - runlength_pos, runlength_pos-1); + return 1; + } +/* + if (writepos - bytes < 0) { + GFXWARN("View %02x:(%d/%d) describes more bytes than needed: %d/%d bytes at rel. offset 0x%04x\n", + id, loop, cel, writepos - bytes, pixmap_size, pos - 1); + bytes = pixmap_size - writepos; + } +*/ + if (op==V1_RLE) + { + color = resource[literal_pos]; + NEXT_LITERAL_BYTE(1); + } + + if (!op && literal_pos + bytes > size) + { + GFXWARN("View %02x:(%d/%d) requires %d bytes to be read when %d are available at pos %d\n", + id, loop, cel, bytes, size - literal_pos, literal_pos-1); + return 1; + } + + while (bytes--) + { + if (op) { + if (op & V1_RLE_BG) { + writepos--; + *(dest + writepos) = color_key; + } else { + writepos--; + *(dest + writepos) = color; + } + } else { + writepos--; + *(dest + writepos) = *(resource + literal_pos); + NEXT_LITERAL_BYTE(1); + + } + if (writepos == linebase) + { + writepos+=2*xl; + linebase+=xl; + } + } + } + } + else { + while (writepos < pixmap_size && literal_pos < size && runlength_pos < size) { + int op = resource[runlength_pos]; + int bytes; + int readbytes = 0; + + NEXT_RUNLENGTH_BYTE(1); + + if (op & V1_RLE) { + bytes = op & 0x3f; + op &= (V1_RLE | V1_RLE_BG); + readbytes = (op & V1_RLE_BG)? 0 : 1; + } else { + readbytes = bytes = op & 0x3f; + op = 0; + } + + if (runlength_pos + readbytes > size) { + return 1; + } + + if (writepos + bytes > pixmap_size) { + GFXWARN("View %02x:(%d/%d) describes more bytes than needed: %d/%d bytes at rel. offset 0x%04x\n", + id, loop, cel, writepos-bytes, pixmap_size, runlength_pos - 1); + bytes = pixmap_size - writepos; + } + + if (!op && literal_pos + bytes > size) + { + GFXWARN("View %02x:(%d/%d) requires %d bytes to be read when %d are available at pos %d\n", + id, loop, cel, bytes, size - literal_pos, literal_pos-1); + return 1; + } + + if (writepos + bytes > pixmap_size) + { + GFXWARN("Writing out of bounds: %d bytes at %d > size %d\n", bytes, writepos, pixmap_size); + } + + if (op) { + if (op & V1_RLE_BG) + memset(dest + writepos, color_key, bytes); + else { + int color = resource[literal_pos]; + + NEXT_LITERAL_BYTE(1); + memset(dest + writepos, color, bytes); + } + } else { + memcpy(dest + writepos, resource + literal_pos, bytes); + NEXT_LITERAL_BYTE(bytes); + } + writepos += bytes; + + } + + }; + + return 0; +} + +static int +decompress_sci_view_amiga(int id, int loop, int cel, byte *resource, byte *dest, int mirrored, int pixmap_size, int size, + int pos, int xl, int yl, int color_key) +{ + int writepos = mirrored? xl - 1 : 0; + + while (writepos < pixmap_size && pos < size) { + int op = resource[pos++]; + int bytes; + int color; + + if (op & 0x07) { + bytes = op & 0x07; + color = op >> 3; + } else { + bytes = op >> 3; + color = color_key; + } + + if (mirrored) { + while (bytes--) { + dest[writepos--] = color; + /* If we've just written the first pixel of a line... */ + if (!((writepos + 1) % xl)) { + /* Then move to the end of next line */ + writepos += 2 * xl; + + if (writepos >= pixmap_size && bytes) + { + GFXWARN("View %02x:(%d/%d) writing out of bounds\n", id, loop, cel); + break; + } + } + } + } else { + if (writepos + bytes > pixmap_size) { + GFXWARN("View %02x:(%d/%d) describes more bytes than needed: %d/%d bytes at rel. offset 0x%04x\n", + id, loop, cel, writepos-bytes, pixmap_size, pos - 1); + bytes = pixmap_size - writepos; + } + memset(dest + writepos, color, bytes); + writepos += bytes; + } + } + + if (writepos < pixmap_size) { + GFXWARN("View %02x:(%d/%d) not enough pixel data in view\n", id, loop, cel); + return 1; + } + + return 0; +} + +gfx_pixmap_t * +gfxr_draw_cel1(int id, int loop, int cel, int mirrored, byte *resource, int size, gfxr_view_t *view, int amiga_game) +{ + int xl = get_int_16(resource); + int yl = get_int_16(resource + 2); + int xhot = (gint8) resource[4]; + int yhot = (guint8) resource[5]; + int pos = 8; + int pixmap_size = xl * yl; + gfx_pixmap_t *retval = gfx_pixmap_alloc_index_data(gfx_new_pixmap(xl, yl, id, loop, cel)); + byte *dest = retval->index_data; + int decompress_failed; + + retval->color_key = resource[6]; + retval->xoffset = (mirrored)? xhot : -xhot; + retval->yoffset = -yhot; + + if (view) { + retval->colors = view->colors; + retval->colors_nr = view->colors_nr; + } + + retval->flags |= GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + + if (xl <= 0 || yl <= 0) { + gfx_free_pixmap(NULL, retval); + GFXERROR("View %02x:(%d/%d) has invalid xl=%d or yl=%d\n", id, loop, cel, xl, yl); + return NULL; + } + + if (amiga_game) + decompress_failed = decompress_sci_view_amiga(id, loop, cel, + resource, dest, mirrored, pixmap_size, size, pos, + xl, yl, retval->color_key); + else + decompress_failed = decompress_sci_view(id, loop, cel, + resource, dest, mirrored, pixmap_size, size, pos, + pos, xl, yl, retval->color_key); + + if (decompress_failed) { + gfx_free_pixmap(NULL, retval); + return NULL; + } + + return retval; +} + +static int +gfxr_draw_loop1(gfxr_loop_t *dest, int id, int loop, int mirrored, byte *resource, int offset, int size, gfxr_view_t *view, int amiga_game) +{ + int i; + int cels_nr = get_int_16(resource + offset); + + if (get_uint_16(resource + offset + 2)) { + GFXWARN("View %02x:(%d): Gray magic %04x in loop, expected white\n", id, loop, get_uint_16(resource + offset + 2)); + } + + if (cels_nr * 2 + 4 + offset > size) { + GFXERROR("View %02x:(%d): Offset array for %d cels extends beyond resource space\n", id, loop, cels_nr); + dest->cels_nr = 0; /* Mark as "delete no cels" */ + dest->cels = NULL; + return 1; + } + + dest->cels = (gfx_pixmap_t**)sci_malloc(sizeof(gfx_pixmap_t *) * cels_nr); + + for (i = 0; i < cels_nr; i++) { + int cel_offset = get_uint_16(resource + offset + 4 + (i << 1)); + gfx_pixmap_t *cel; + + if (cel_offset >= size) { + GFXERROR("View %02x:(%d/%d) supposed to be at illegal offset 0x%04x\n", id, loop, i, cel_offset); + cel = NULL; + } else + cel = gfxr_draw_cel1(id, loop, i, mirrored, resource + cel_offset, size - cel_offset, view, amiga_game); + + if (!cel) { + dest->cels_nr = i; + return 1; + } + + dest->cels[i] = cel; + } + + dest->cels_nr = cels_nr; + + return 0; +} + + +#define V1_FIRST_MAGIC 1 +#define V1_MAGICS_NR 5 +/*static byte view_magics[V1_MAGICS_NR] = {0x80, 0x00, 0x00, 0x00, 0x00};*/ + +gfxr_view_t * +gfxr_draw_view1(int id, byte *resource, int size, gfx_pixmap_color_t *static_pal, + int static_pal_nr) +{ + int i; + int palette_offset; + gfxr_view_t *view; + int mirror_mask; + int amiga_game = 0; + + if (size < V1_FIRST_LOOP_OFFSET + 8) { + GFXERROR("Attempt to draw empty view %04x\n", id); + return NULL; + } + + view = (gfxr_view_t*)sci_malloc(sizeof(gfxr_view_t)); + view->ID = id; + view->flags = 0; + + view->loops_nr = resource[V1_LOOPS_NR_OFFSET]; + palette_offset = get_uint_16(resource + V1_PALETTE_OFFSET); + mirror_mask = get_uint_16(resource + V1_MIRROR_MASK); + + if (view->loops_nr * 2 + V1_FIRST_LOOP_OFFSET > size) { + GFXERROR("View %04x: Not enough space in resource to accomodate for the claimed %d loops\n", id, view->loops_nr); + free(view); + return NULL; + } + +/* fprintf(stderr, "View flags are 0x%02x\n", resource[3]);*/ + +/* + for (i = 0; i < V1_MAGICS_NR; i++) + if (resource[V1_FIRST_MAGIC + i] != view_magics[i]) { + GFXWARN("View %04x: View magic #%d should be %02x but is %02x\n", + id, i, view_magics[i], resource[V1_FIRST_MAGIC + i]); + } +*/ + + if (palette_offset > 0) + { + if (palette_offset > size) { + GFXERROR("Palette is outside of view %04x\n", id); + free(view); + return NULL; + } + if (!(view->colors = gfxr_read_pal1(id, &(view->colors_nr), + resource + palette_offset, size - palette_offset))) { + GFXERROR("view %04x: Palette reading failed. Aborting...\n", id); + free(view); + return NULL; + } + } else if (static_pal_nr == GFX_SCI1_AMIGA_COLORS_NR) { + /* Assume we're running an amiga game. */ + amiga_game = 1; + view->colors = static_pal; + view->colors_nr = static_pal_nr; + view->flags |= GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + } else { + GFXWARN("view %04x: Doesn't have a palette. Can FreeSCI handle this?\n", view->ID); + view->colors = NULL; + view->colors_nr = 0; + } + + view->loops = (gfxr_loop_t*)sci_malloc(sizeof (gfxr_loop_t) * view->loops_nr); + + for (i = 0; i < view->loops_nr; i++) { + int error_token = 0; + int loop_offset = get_uint_16(resource + V1_FIRST_LOOP_OFFSET + (i << 1)); + + if (loop_offset >= size) { + GFXERROR("View %04x:(%d) supposed to be at illegal offset 0x%04x\n", id, i); + error_token = 1; + } + + if (error_token || gfxr_draw_loop1(view->loops + i, id, i, mirror_mask & (1<loops_nr = i; + gfxr_free_view(NULL, view); + return NULL; + } + } + + return view; +} + +#define V2_HEADER_SIZE 0 +#define V2_LOOPS_NUM 2 +#define V2_PALETTE_OFFSET 8 +#define V2_BYTES_PER_LOOP 12 +#define V2_BYTES_PER_CEL 13 + +#define V2_IS_MIRROR 1 +#define V2_COPY_OF_LOOP 2 +#define V2_CELS_NUM 4 +#define V2_LOOP_OFFSET 14 + +#define V2_CEL_WIDTH 0 +#define V2_CEL_HEIGHT 2 +#define V2_X_DISPLACEMENT 4 +#define V2_Y_DISPLACEMENT 6 +#define V2_COLOR_KEY 8 +#define V2_RUNLENGTH_OFFSET 24 +#define V2_LITERAL_OFFSET 28 + +gfx_pixmap_t * +gfxr_draw_cel11(int id, int loop, int cel, int mirrored, byte *resource_base, byte *cel_base, int size, gfxr_view_t *view) +{ + int xl = get_uint_16(cel_base + V2_CEL_WIDTH); + int yl = get_uint_16(cel_base + V2_CEL_HEIGHT); + int xdisplace = get_uint_16(cel_base + V2_X_DISPLACEMENT); + int ydisplace = get_uint_16(cel_base + V2_Y_DISPLACEMENT); + int runlength_offset = get_uint_16(cel_base + V2_RUNLENGTH_OFFSET); + int literal_offset = get_uint_16(cel_base + V2_LITERAL_OFFSET); + int pixmap_size = xl * yl; + + gfx_pixmap_t *retval = gfx_pixmap_alloc_index_data(gfx_new_pixmap(xl, yl, id, loop, cel)); + byte *dest = retval->index_data; + int decompress_failed; + + retval->color_key = cel_base[V2_COLOR_KEY]; + retval->xoffset = (mirrored)? xdisplace : -xdisplace; + retval->yoffset = -ydisplace; + + if (view) { + retval->colors = view->colors; + retval->colors_nr = view->colors_nr; + } + + retval->flags |= GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + + if (xl <= 0 || yl <= 0) { + gfx_free_pixmap(NULL, retval); + GFXERROR("View %02x:(%d/%d) has invalid xl=%d or yl=%d\n", id, loop, cel, xl, yl); + return NULL; + } + + decompress_failed = decompress_sci_view(id, loop, cel, resource_base, dest, mirrored, pixmap_size, size, + runlength_offset, literal_offset, xl, yl, retval->color_key); + + if (decompress_failed) { + gfx_free_pixmap(NULL, retval); + return NULL; + } + + return retval; +} + +gfxr_loop_t * +gfxr_draw_loop11(int id, int loop, int mirrored, byte *resource_base, byte *loop_base, int size, int cels_nr, + gfxr_loop_t *result, gfxr_view_t *view, int bytes_per_cel) +{ + byte *seeker = loop_base; + int i; + + result->cels_nr = cels_nr; + result->cels = (gfx_pixmap_t **) + sci_malloc(sizeof(gfx_pixmap_t *) * cels_nr); + + for (i = 0; i < cels_nr; i++) + { + result->cels[i] = gfxr_draw_cel11(id, loop, i, mirrored, resource_base, seeker, size, view); + seeker += bytes_per_cel; + } + + return result; +} + +gfxr_view_t * +gfxr_draw_view11(int id, byte *resource, int size) +{ + gfxr_view_t *view; + int header_size = get_uint_16(resource + V2_HEADER_SIZE); + int palette_offset = get_uint_16(resource + V2_PALETTE_OFFSET); + int bytes_per_loop = resource[V2_BYTES_PER_LOOP]; + int loops_num = resource[V2_LOOPS_NUM]; + int bytes_per_cel = resource[V2_BYTES_PER_CEL]; + int i; + byte *seeker; + + view = (gfxr_view_t*)sci_malloc(sizeof(gfxr_view_t)); + + memset(view, 0, sizeof(gfxr_view_t)); + view->ID = id; + view->flags = 0; + + view->loops_nr = loops_num; + view->loops = (gfxr_loop_t *)calloc(view->loops_nr, sizeof(gfxr_loop_t)); + + /* There is no indication of size here, but this is certainly large enough */ + view->colors = gfxr_read_pal11(id, &view->colors_nr, resource + palette_offset, 1284); + + seeker = resource + header_size; + for (i = 0; i < view->loops_nr; i++) + { + static char *truth[2] = {"not ",""}; + int loop_offset = get_uint_16(seeker + V2_LOOP_OFFSET); + int cels = seeker[V2_CELS_NUM]; + int mirrored = seeker[V2_IS_MIRROR]; + int copy_entry = seeker[V2_COPY_OF_LOOP]; + + printf("%d\n", mirrored); + if (copy_entry == 255) + gfxr_draw_loop11(id, i, 0, resource, resource + loop_offset, size, cels, view->loops + i, + view, bytes_per_cel); else + { + byte *temp = resource + header_size + copy_entry * bytes_per_loop; + loop_offset = get_uint_16(temp + V2_LOOP_OFFSET); + cels = temp[V2_CELS_NUM]; + gfxr_draw_loop11(id, i, 1, resource, resource + loop_offset, size, cels, + view->loops + i, view, bytes_per_cel); + } + + seeker += bytes_per_loop; + } + + return view; +} diff --git a/engines/sci/gfx/sbtree.c b/engines/sci/gfx/sbtree.c new file mode 100644 index 0000000000..b5e2c6b068 --- /dev/null +++ b/engines/sci/gfx/sbtree.c @@ -0,0 +1,473 @@ +/*************************************************************************** + sbtree.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Static binary lookup tree lookup */ + + +#include +#include +#include +#include + +#define NOT_A_KEY -1 + +typedef struct { + int key; + void *value; +} sbcell_t; + +int +int_compar(const void *a, const void *b) +{ + return (*((int *)a))-(*((int *)b)); +} + + +void +insert_interval(sbcell_t *data, int start, int stop, int *keys, int plus) +{ + int center = start + ((stop - start) >> 1); + + data->key = keys[center]; + + if (start == stop) + return; + + if (center > start) + insert_interval(data + plus, start, center - 1, keys, plus << 1); + + if (center < stop) + insert_interval(data + plus + 1, center + 1, stop, keys, ((plus << 1) + 1)); +} + +sbtree_t * +sbtree_new(int size, int *keys) +{ + int table_size = 2; + int levels = 0; + sbcell_t *table; + sbtree_t *tree; + int i; + + if (size < 0) + return NULL; + + while (table_size <= size) { + table_size <<= 1; + ++levels; + } + + if (table_size > 1) + --table_size; + + table = (sbcell_t*)sci_calloc(sizeof(sbcell_t), table_size); + for (i = 0; i < table_size; i++) + table[i].key = NOT_A_KEY; + + if (!table) { + fprintf(stderr,"SBTree: Out of memory: Could not allocate %d cells\n", table_size); + return NULL; + } + + tree = (sbtree_t*)sci_malloc(sizeof(sbtree_t)); + + if (!tree) { + fprintf(stderr,"SBTree: Could not allocate tree structure\n"); + free(table); + return NULL; + } + + qsort(keys, size, sizeof(int), int_compar); + + insert_interval(table, 0, size-1, keys, 1); + + tree->levels = levels; + tree->entries_nr = size; + if ((tree->min_entry = keys[0]) < 0) { + fprintf(stderr,"SBTree: Error: Using negative keys\n"); + free(table); + free(tree); + return NULL; + } + tree->max_entry = keys[size - 1]; + tree->data = (void *) table; + tree->alloced_entries = table_size; + return tree; +} + + +void +sbtree_free(sbtree_t *tree) +{ + if (!tree) { + fprintf(stderr,"SBTree: Attempt to free NULL sbtree\n"); + return; + } + + free(tree->data); + free(tree); +} + + +void +sbtree_foreach(sbtree_t *tree, void *args, void *(*operation)(sbtree_t *, const int, + const void *, void *)) +{ + int i; + sbcell_t *cell = (sbcell_t *) tree->data; + + for (i = 0; i < tree->alloced_entries; i++) { + if (cell->key != NOT_A_KEY) + cell->value = operation(tree, cell->key, cell->value, args); + cell = cell + 1; + } +} + +sbcell_t * +locate(sbcell_t *start, int key, int level, int levels, int plus) +{ + int comparison; + + if (level >= levels && (level != levels || start->key == NOT_A_KEY)) + /* For large tables, the speed improvement caused by this comparison + ** scheme is almost (cough) measurable... + */ + return NULL; + + comparison = key - start->key; + + if (!comparison) + return start; + + return locate(start + plus + (comparison > 0), key, level + 1, levels, (plus << 1) + (comparison > 0)); +} + + +int +sbtree_set(sbtree_t *tree, int key, void *value) +{ + sbcell_t *cell = locate((sbcell_t *) tree->data, key, 0, tree->levels, 1); + + if (cell) + cell->value = value; + else + return -1; + + return 0; +} + + +void * +sbtree_get(sbtree_t *tree, int key) +{ + sbcell_t *cell = locate((sbcell_t *) tree->data, key, 0, tree->levels, 1); + + if (cell) + return cell->value; + else + return NULL; +} + +static void +sbtree_print(sbtree_t *tree) +{ + int l, i; + sbcell_t *cells = (sbcell_t *) tree->data; + + fprintf(stderr,"\tTree:\n"); + for (l = 0; l <= tree->levels; l++) { + fprintf(stderr,"\t "); + for (i = 0; i < (1 << l); i++) { + if (cells->key == NOT_A_KEY) + fprintf(stderr,"-- "); + else { + if (cells->value) + fprintf(stderr,"%d+ ", cells->key); + else + fprintf(stderr,"%d ", cells->key); + } + + cells = cells + 1; + } + fprintf(stderr,"\n"); + } + fprintf(stderr,"\n"); +} + + + +/***************************** TEST CODE ********************************/ + + +#ifdef SBTREE_DEBUG + +static int any_error; + + +void * +foreach_double_func(sbtree_t *tree, const int key, const void *value, void *args) +{ + int *real_value = (int *) value; + + if (!real_value) + fprintf(stderr,"foreach_double_func(): key %d mapped to non-value!\n", key); + else *real_value *= 2; + + return real_value; +} + +int * +generate_linear_forward(int numbers) +{ + int i; + int *data = sci_malloc(sizeof(int) * numbers); + for (i = 0; i < numbers; i++) + data[i] = i + 1; + + return data; +} + +int * +generate_linear_backward(int numbers) +{ + int i; + int *data = sci_malloc(sizeof(int) * numbers); + for (i = 0; i < numbers; i++) + data[i] = numbers - i; + + return data; +} + +int * +generate_random(int numbers, int max) +{ + int i; + int *data = sci_malloc(sizeof(int) * numbers); + + for (i = 0; i < numbers; i++) + data[i] = 1 + (int) ((rand() * 1.0 * max) / (RAND_MAX + 1.0)); + + return data; +} + + +void +insert_values(sbtree_t *tree, int nr, int *data) +{ + int i; + + for (i = 0; i < nr; i++) + if (sbtree_set(tree, data[i], (void *)(data + i))) { + fprintf(stderr, "While inserting: %d incorrectly deemed invalid\n", data[i]); + any_error = 1; + } +} + + +#define MODE_LINEAR 0 +#define MODE_LINEAR_MAP 1 +#define MODE_RANDOM 2 +#define MODE_LINEAR_DOUBLE 3 + +void +test_value(sbtree_t *tree, int times, int max, int numbers, int *data, int mode) +{ + int i; + int failed = 0; + + for (i = 0; i < times; i++) { + int key = (mode == MODE_LINEAR || mode == MODE_LINEAR_DOUBLE)? i : + (mode == MODE_LINEAR_MAP)? data[i % numbers] : + (int) ((rand() * 1.0 * max) / (RAND_MAX + 1.0)); + int *value = (int *) sbtree_get(tree, (mode == MODE_LINEAR_DOUBLE)? key >> 1 : key); + int found = 0; + int j; + + for (j = 0; j < numbers && !found; j++) + if (data[j] == key) + found = 1; + + if (found && !value) { + fprintf(stderr, "!%d ", key); + ++failed; + } + else if (!found && found) { + fprintf(stderr, "?[%d]=%d ", key, *value); + ++failed; + } + } + + if (failed) + fprintf(stderr, "(%d/%d errors)\n", any_error = failed, times); + else + fprintf(stderr, "OK\n"); +} + + +void +test_boundary(sbtree_t *tree, int max, int random) +{ + int *value_too_low = sbtree_get(tree, 0); + int *value_too_high = sbtree_get(tree, max + 1); + int *value_low = sbtree_get(tree, 1); + int *value_high = sbtree_get(tree, max); + int failure = (value_too_low || value_too_high || (!random && (!value_low || !value_high))); + + if (!failure) + fprintf(stderr, "OK\n"); + else { + any_error = 1; + + fprintf(stderr, "Errors: "); + if (value_too_low) + fprintf(stderr, "too-low=%d ", *value_too_low); + if (value_too_high) + fprintf(stderr, "too-high=%d ", *value_too_high); + + if (!random) { + if (!value_low) + fprintf(stderr, "!low "); + if (!value_high) + fprintf(stderr, "!high "); + } + fprintf(stderr, "\n"); + } +} + + +void +test_empty(sbtree_t *tree, int count, int max) +{ + int i; + int errors = 0; + + for (i = 0; i < count; i++) { + int key = 1 + (int) ((rand() * 1.0 * max) / (RAND_MAX + 1.0)); + int *value; + + if ((value = (int *) sbtree_get(tree, key))) { + fprintf(stderr, "?[%d]=%d\n", key, *value); + ++errors; + } + } + + if (errors) + fprintf(stderr," (%d/%d errors)\n", any_error = errors, count); + else + fprintf(stderr,"OK\n"); +} + +void +run_test(sbtree_t *tree, int entries, int *data, int random, int max_value) +{ + char *tests[] = {"\tLinear reference test: \t\t", "\tKey map reference test: \t", "\tRandom access test: \t\t"}; + int i; + + any_error = 0; + + fprintf(stderr, "\tEmpty test: \t\t\t"); + test_empty(tree, entries * 2, entries + 1); + insert_values(tree, entries, data); + fprintf(stderr, "\tBoundary test: \t\t\t"); + test_boundary(tree, max_value, random); + + for (i = 0; i < 3; i++) { + fprintf(stderr, tests[i]); + test_value(tree, entries * 2, entries * 2, entries, data, i); + } + + if (!random) { + i = data[0]; + sbtree_foreach(tree, NULL, foreach_double_func); + fprintf(stderr, "\tForeach test: \t\t\t"); + if (i * 2 != data[0]) { + fprintf(stderr,"Error: No effect: %d * 2 != %d\n", i, data[0]); + any_error = 1; + } else + test_value(tree, entries * 2, entries * 2, entries, data, MODE_LINEAR_DOUBLE); + } + + if (any_error) + sbtree_print(tree); + + free(data); + sbtree_free(tree); +} + + +#define TESTS_NR 11 + +int +main(int argc, char **argv) +{ + int tests_nr = TESTS_NR; + int test_sizes[TESTS_NR] = {1, 2, 3, 7, 8, 9, 1000, 16383, 16384, 16385, 1000000}; + int i; + fprintf(stderr, "sbtree.c Copyright (C) 2000 Christoph Reichenbach \n" + "This program is provided WITHOUT WARRANTY of any kind\n" + "Please refer to the file COPYING that should have come with this program\n"); + fprintf(stderr, "Static Binary Tree testing facility\n"); + + free(malloc(42)); /* Make sure libefence's Copyright message is print here if we're using it */ + + fprintf(stderr,"\nsbtree.c: Running %d tests.\n", tests_nr); + + for (i = 0; i < tests_nr; i++) { + int entries = test_sizes[i]; + sbtree_t *tree; + int *data; + + fprintf(stderr,"Test #%d: %d entries\n", i+1, entries); + + fprintf(stderr,"\t%da: Linear values\n", i+1); + data = generate_linear_forward(entries); + tree = sbtree_new(entries, data); + run_test(tree, entries, data, 0, entries); + + fprintf(stderr,"\t%db: Reverse linear values\n", i+1); + data = generate_linear_backward(entries); + tree = sbtree_new(entries, data); + run_test(tree, entries, data, 0, entries); + + fprintf(stderr,"\t%dc: Dense random values\n", i+1); + data = generate_random(entries, 1 + (entries >> 2)); + tree = sbtree_new(entries, data); + run_test(tree, entries, data, 1, 1 + (entries >> 2)); + + fprintf(stderr,"\t%dc: Sparse random values\n", i+1); + data = generate_random(entries, (entries << 2)); + tree = sbtree_new(entries, data); + run_test(tree, entries, data, 1, entries << 2); + + fprintf(stderr,"Test #%d completed.\n\n", i+1); + } + + fprintf(stderr,"Test suite completed.\n"); + return 0; +} + + +#endif /* SBTREE_DEBUG */ diff --git a/engines/sci/gfx/sci_widgets.c b/engines/sci/gfx/sci_widgets.c new file mode 100644 index 0000000000..fe4cfda1a3 --- /dev/null +++ b/engines/sci/gfx/sci_widgets.c @@ -0,0 +1,765 @@ +/*************************************************************************** + sci_widgets.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include +#include +#include +#include + +#define SCI_SPECIAL_CHAR_ARROW_UP 0x18 +#define SCI_SPECIAL_CHAR_ARROW_DOWN 0x19 + + +static void +clear_titlebar(gfxw_port_t *titlebar) +{ + if (titlebar->contents) { + titlebar->contents->widfree(titlebar->contents); + titlebar->contents = NULL; + titlebar->nextpp = &(titlebar->contents); + } +} + +static gfxw_list_t * +make_titlebar_list(state_t *s, rect_t bounds, gfxw_port_t *status_bar) +{ + gfx_color_t color = status_bar->bgcolor; + gfxw_list_t *list; + gfxw_box_t *bgbox; + + + list = gfxw_new_list(status_bar->bounds, 0); + bgbox = gfxw_new_box(s->gfx_state, gfx_rect(0, 0, status_bar->bounds.xl, status_bar->bounds.yl - 1), + color, color, GFX_BOX_SHADE_FLAT); + + list->add((gfxw_container_t *) list, (gfxw_widget_t *) bgbox); + + return list; +} + +static gfxw_list_t * +finish_titlebar_list(state_t *s, gfxw_list_t *list, gfxw_port_t *status_bar) +{ + gfx_color_t black = s->ega_colors[0]; + gfxw_primitive_t *line; + + line = gfxw_new_line(gfx_point(0, status_bar->bounds.yl - 1), + gfx_point(status_bar->bounds.xl, status_bar->bounds.yl - 1), + black, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL); + list->add((gfxw_container_t *) list, (gfxw_widget_t *) line); + + return list; +} + +void +sciw_set_status_bar(state_t *s, gfxw_port_t *status_bar, char *text, int fgcolor, int bgcolor) +{ + gfx_state_t *state; + gfxw_list_t *list; + gfx_color_t bg = status_bar->bgcolor; + gfx_color_t fg = status_bar->color; + gfx_color_t black = s->ega_colors[0]; + + if (!status_bar->visual) { + GFXERROR("Attempt to change title bar without visual!\n"); + return; + } + + state = status_bar->visual->gfx_state; + + if (!state) { + GFXERROR("Attempt to change title bar with stateless visual!\n"); + return; + } + + clear_titlebar(status_bar); + + if (text) { + gfxw_text_t *textw = gfxw_new_text(state, gfx_rect(0, 0, status_bar->bounds.xl, status_bar->bounds.yl), + status_bar->font_nr, text, ALIGN_LEFT, ALIGN_CENTER, + fg, fg, bg, GFXR_FONT_FLAG_NO_NEWLINES); + + list = make_titlebar_list(s, status_bar->bounds, status_bar); + + list->add((gfxw_container_t *) list, (gfxw_widget_t *) textw); + + } else { + gfxw_box_t *bgbox = gfxw_new_box(state, gfx_rect(0, 0, status_bar->bounds.xl, status_bar->bounds.yl - 1), + black, black, GFX_BOX_SHADE_FLAT); + + list = gfxw_new_list(status_bar->bounds, 0); + + list->add((gfxw_container_t *) list, (gfxw_widget_t *) bgbox); + } + + list->add(GFXWC(status_bar), GFXW(list)); + finish_titlebar_list(s, list, status_bar); + + status_bar->draw(GFXW(status_bar), gfxw_point_zero); + gfxop_update(state); +} + +static void +sciw_make_window_fit(rect_t *rect, gfxw_port_t *parent) +{ + /* This window is meant to cover the whole screen, so we allow it to go through. */ + if (rect->xl == 319 && rect->yl == 189) return; + + if (rect->x + rect->xl > parent->bounds.x + parent->bounds.xl) + rect->x -= (rect->x + rect->xl) - (parent->bounds.x + parent->bounds.xl) + 2; + + if (rect->y + rect->yl > parent->bounds.y + parent->bounds.yl) + rect->y -= (rect->y + rect->yl) - (parent->bounds.y + parent->bounds.yl) + 2; +} + +gfxw_port_t * +sciw_new_window(state_t *s, rect_t area, int font, gfx_color_t color, gfx_color_t bgcolor, + int title_font, gfx_color_t title_color, gfx_color_t title_bgcolor, + const char *title, int flags) +{ + gfxw_visual_t *visual = s->visual; + gfx_state_t *state = s->gfx_state; + int shadow_offset = 2; + rect_t frame; + gfx_color_t black = {0}; + gfxw_port_t *win; + gfxw_list_t *decorations; + int xextra = !(flags & WINDOW_FLAG_NOFRAME) ? 1 : 0; + int yextra = !(flags & WINDOW_FLAG_NOFRAME) ? 2 : 0; + + if (area.xl == 319 && area.yl == 189) + { + flags |= WINDOW_FLAG_NOFRAME; + /* The below line makes the points bar in QfG2 work, but breaks + the one in QfG1. Hm. */ + if (bgcolor.priority == 255) /* Yep, QfG2 */ + area.y += 3; + } + + /* + if (area.y + area.yl > visual->bounds.y + visual->bounds.yl) + { + area.y -= (area.y + area.yl) - (visual->bounds.y + visual->bounds.yl) + yextra; + } + + if (area.x + area.xl > visual->bounds.x + visual->bounds.xl) + { + area.x -= (area.x + area.xl) - (visual->bounds.x + visual->bounds.xl) + xextra; + } + */ + + if (flags & WINDOW_FLAG_TITLE) + area. y += 10; + + if (!(flags & (WINDOW_FLAG_TITLE | WINDOW_FLAG_NOFRAME))) + area.yl -= 1; /* Normal windows are drawn one pixel too small. */ + + sciw_make_window_fit(&area, s->wm_port); + win = gfxw_new_port(visual, s->wm_port, area, color, bgcolor); + + win->font_nr = font; + win->title_text = title; + win->port_flags = flags; + + win->flags |= GFXW_FLAG_IMMUNE_TO_SNAPSHOTS; + + if (flags & WINDOW_FLAG_DONTDRAW) + flags = WINDOW_FLAG_TRANSPARENT | WINDOW_FLAG_NOFRAME; + + if (flags == (WINDOW_FLAG_TRANSPARENT | WINDOW_FLAG_NOFRAME)) + return win; /* Fully transparent window */ + + if (flags & WINDOW_FLAG_TITLE) + frame = gfx_rect(area.x-1, area.y-10, area.xl + 2, area.yl + 11); + else + frame = gfx_rect(area.x-1, area.y-1, area.xl + 2, area.yl + 2); + + /* Set visible window boundaries */ + win->bounds = gfx_rect(frame.x, frame.y, frame.xl + shadow_offset, frame.yl + shadow_offset); + + decorations = gfxw_new_list(gfx_rect(frame.x, frame.y, + frame.xl + 1 + shadow_offset, frame.yl + 1 + shadow_offset), 0); + + if (!(flags & WINDOW_FLAG_TRANSPARENT)) { + /* Draw window background */ + win->port_bg = (gfxw_widget_t *) gfxw_new_box (state, + gfx_rect(1, (flags & WINDOW_FLAG_TITLE)? 10 : 1, + area.xl, area.yl), + bgcolor, bgcolor, GFX_BOX_SHADE_FLAT); + decorations->add((gfxw_container_t *) decorations, win->port_bg); + win->flags |= GFXW_FLAG_OPAQUE; + } + + if (flags & WINDOW_FLAG_TITLE) { + /* Add window title */ + rect_t title_rect = gfx_rect(1, 1, area.xl, 8); + + decorations->add((gfxw_container_t *) decorations, (gfxw_widget_t *) + gfxw_new_box(state, title_rect, title_bgcolor, title_bgcolor, GFX_BOX_SHADE_FLAT)); + + decorations->add((gfxw_container_t *) decorations, (gfxw_widget_t *) + gfxw_new_text(state, title_rect, title_font, title, + ALIGN_CENTER, ALIGN_CENTER, title_color, title_color, + title_bgcolor, GFXR_FONT_FLAG_NO_NEWLINES)); + } + + if (!(flags & WINDOW_FLAG_NOFRAME)) { + /* Draw backdrop shadow */ + + if (!(flags & WINDOW_FLAG_NO_DROP_SHADOW)) { + if (gfxop_set_color(state, &black, 0, 0, 0, 0x80, bgcolor.priority, -1)) { + GFXERROR("Could not get black/semitrans color entry!\n"); + return NULL; + } + + decorations->add((gfxw_container_t *) decorations, (gfxw_widget_t *) + gfxw_new_box(state, gfx_rect(shadow_offset + 1, frame.yl - 1, + frame.xl - 4, shadow_offset), + black, black, GFX_BOX_SHADE_FLAT)); + + decorations->add((gfxw_container_t *) decorations, (gfxw_widget_t *) + gfxw_new_box(state, gfx_rect(frame.xl - 1, shadow_offset + 1, + shadow_offset, frame.yl - 2), + black, black, GFX_BOX_SHADE_FLAT)); + } + + /* Draw frame */ + + if (gfxop_set_color(state, &black, 0, 0, 0, 0, bgcolor.priority, -1)) { + GFXERROR("Could not get black color entry!\n"); + return NULL; + } + + if (!(flags & WINDOW_FLAG_NO_DROP_SHADOW)) { + + decorations->add((gfxw_container_t *) decorations, (gfxw_widget_t *) + gfxw_new_rect(gfx_rect(0, 0, frame.xl-1, frame.yl-1), + black, GFX_LINE_MODE_FINE, GFX_LINE_STYLE_NORMAL)); + + if (flags & WINDOW_FLAG_TITLE) + decorations->add((gfxw_container_t *) decorations, (gfxw_widget_t *) + gfxw_new_line(gfx_point(1, 9), + gfx_point(frame.xl - 2, 9), + black, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL)); + } else { + decorations->add((gfxw_container_t *) decorations, (gfxw_widget_t *) + gfxw_new_rect(gfx_rect(0, 0, frame.xl, frame.yl), + black, GFX_LINE_MODE_FINE, GFX_LINE_STYLE_NORMAL)); + } + + } + + win->decorations = decorations; + decorations->parent = GFXWC(win); + + return win; +} + + + +/*----------------*/ +/**** Controls ****/ +/*----------------*/ +static inline rect_t +_move_and_extend_rect(rect_t rect, point_t point, int yplus) +{ + return gfx_rect(rect.x + point.x, rect.y + point.y, rect.xl + 1, rect.yl + yplus); +} + + +gfxw_list_t * +_sciw_add_text_to_list(gfxw_list_t *list, gfxw_port_t *port, rect_t zone, char *text, + int font, gfx_alignment_t align, char framed, char inverse, int flags, + char gray_text) +{ + gfx_color_t *color1, *color2, *bgcolor = {0}; + + if (inverse) { + color1 = color2 = &(port->bgcolor); + bgcolor = &(port->color); + } else if (gray_text) { + bgcolor = color1 = &(port->bgcolor); + color2 = &(port->color); + } else { + color1 = color2 = &(port->color); + bgcolor = &(port->bgcolor); + } + + list->add(GFXWC(list), GFXW(gfxw_new_text(port->visual->gfx_state, zone, + font, text, align, ALIGN_TOP, + *color1, *color2, *bgcolor, flags))); + + + zone.xl--; + zone.yl -= 2; + + if (framed) { + list->add(GFXWC(list), + GFXW(gfxw_new_rect(zone, *color2, GFX_LINE_MODE_CORRECT, + GFX_LINE_STYLE_STIPPLED))); + } + + return list; +} + +gfxw_list_t * +sciw_new_button_control(gfxw_port_t *port, reg_t ID, rect_t zone, char *text, int font, char selected, char inverse, char grayed_out) +{ + gfx_color_t *frame_col = (inverse)? &(port->bgcolor) : &(port->color); + gfxw_list_t *list; + + zone.x--; + zone.y--; + zone.xl++; + zone.yl++; + + list = gfxw_new_list(_move_and_extend_rect(zone, gfx_point(port->zone.x, port->zone.y), 1), 0); + + gfxw_set_id(GFXW(list), ID.segment, ID.offset); + + zone.x = 0; + zone.y = 0; + + if (inverse) + list->add(GFXWC(list), GFXW(gfxw_new_box(NULL, gfx_rect(zone.x, zone.y, zone.xl + 1, zone.yl + 1), + port->color, port->color, GFX_BOX_SHADE_FLAT))); + + if (!inverse) + list = _sciw_add_text_to_list(list, port, gfx_rect(zone.x + 1, zone.y + 2, zone.xl - 1, zone.yl), + text, font, ALIGN_CENTER, 0, inverse, GFXR_FONT_FLAG_EAT_TRAILING_LF, grayed_out); + + if (!inverse) + list->add(GFXWC(list), + GFXW(gfxw_new_rect(zone, *frame_col, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL))); + + if (inverse) + list = _sciw_add_text_to_list(list, port, gfx_rect(zone.x + 1, zone.y + 2, zone.xl - 1, zone.yl), + text, font, ALIGN_CENTER, 0, inverse, GFXR_FONT_FLAG_EAT_TRAILING_LF, grayed_out); + + if (selected) + list->add(GFXWC(list), + GFXW(gfxw_new_rect(gfx_rect(zone.x + 1, zone.y + 1, zone.xl - 2, zone.yl - 2), + *frame_col, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL))); + + return list; +} + + +gfxw_list_t * +sciw_new_text_control(gfxw_port_t *port, reg_t ID, rect_t zone, char *text, int font, + gfx_alignment_t align, char framed, char inverse) +{ + gfxw_list_t *list = gfxw_new_list(_move_and_extend_rect(zone, gfx_point(port->zone.x, port->zone.y), 2), 0); + + gfxw_set_id(GFXW(list), ID.segment, ID.offset); + + zone.x = 0; + zone.y = 0; + + return _sciw_add_text_to_list(list, port, zone, text, font, align, framed, inverse, 0, port->gray_text); +} + + +gfxw_list_t * +sciw_new_edit_control(gfxw_port_t *port, reg_t ID, rect_t zone, char *text, int font, unsigned int cursor, + char inverse) +{ + gfxw_text_t *text_handle; + long draw_cursor; + long foo; + + gfxw_list_t *list; + int cursor_height = gfxop_get_font_height(port->visual->gfx_state, font); + + zone.x--; + zone.y--; + zone.xl++; + zone.yl++; + + list = gfxw_new_list(_move_and_extend_rect(zone, gfx_point(port->zone.x, port->zone.y), 1), 0); + gfxw_set_id(GFXW(list), ID.segment, ID.offset); + zone.x = 1; + zone.y = 1; + + sci_gettime(&foo, &draw_cursor); + draw_cursor = draw_cursor > 500000; + + if (!draw_cursor) { + text_handle = gfxw_new_text(port->visual->gfx_state, zone, + font, text, ALIGN_LEFT, ALIGN_TOP, + port->color, port->color, port->bgcolor, GFXR_FONT_FLAG_NO_NEWLINES); + + list->add(GFXWC(list), GFXW(text_handle)); + } else { + char *textdup = (char*)sci_malloc(strlen(text) + 1); + + strncpy(textdup, text, cursor); + + if (cursor <= strlen(text)) + textdup[cursor] = 0; /* terminate */ + + if (cursor > 0) { + text_handle = gfxw_new_text(port->visual->gfx_state, zone, + font, textdup, ALIGN_LEFT, ALIGN_TOP, + port->color, port->color, port->bgcolor, GFXR_FONT_FLAG_NO_NEWLINES); + + list->add(GFXWC(list), GFXW(text_handle)); + zone.x += text_handle->width; + } + + if (cursor < strlen(text)) { + textdup[0] = text[cursor]; + textdup[1] = 0; + text_handle = gfxw_new_text(port->visual->gfx_state, zone, + font, textdup, ALIGN_LEFT, ALIGN_TOP, + port->bgcolor, port->bgcolor, port->color, GFXR_FONT_FLAG_NO_NEWLINES); + list->add(GFXWC(list), GFXW(text_handle)); + zone.x += text_handle->width; + }; + + if (cursor+1 < strlen(text)) { + text_handle = gfxw_new_text(port->visual->gfx_state, zone, + font, text + cursor + 1, ALIGN_LEFT, ALIGN_TOP, + port->color, port->color, port->bgcolor, GFXR_FONT_FLAG_NO_NEWLINES); + list->add(GFXWC(list), GFXW(text_handle)); + zone.x += text_handle->width; + }; + + if (cursor == strlen(text)) + list->add(GFXWC(list), GFXW(gfxw_new_line(gfx_point(zone.x, zone.y), + gfx_point(zone.x, zone.y + cursor_height - 1), + port->color, GFX_LINE_MODE_FAST, GFX_LINE_STYLE_NORMAL))); + free(textdup); + } + + + zone.x = zone.y = 0; + + list->add(GFXWC(list), + GFXW(gfxw_new_rect(zone, port->color, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL))); + + return list; +} + + +gfxw_list_t * +sciw_new_icon_control(gfxw_port_t *port, reg_t ID, rect_t zone, int view, int loop, int cel, + char frame, char inverse) +{ + gfxw_list_t *list = gfxw_new_list(_move_and_extend_rect(zone, gfx_point(port->zone.x, port->zone.y), 1), 0); + gfxw_widget_t *icon; + gfxw_set_id(GFXW(list), ID.segment, ID.offset); + + if (!port->visual) { + GFXERROR("Attempting to create icon control for virtual port!\n"); + return NULL; + } + + zone.x = 0; + zone.y = 0; + + icon = GFXW(gfxw_new_view(port->visual->gfx_state, gfx_point(zone.x, zone.y), view, loop, cel, 0, -1, -1, + ALIGN_LEFT, ALIGN_TOP, GFXW_VIEW_FLAG_DONT_MODIFY_OFFSET)); + + if (!icon) { + GFXERROR("Attempt to create icon control with cel %d/%d/%d (invalid)\n", view, loop, cel); + return NULL; + } + + list->flags |= GFXW_FLAG_MULTI_ID; + + list->add(GFXWC(list), icon); + + return list; +} + + +gfxw_list_t * +sciw_new_list_control(gfxw_port_t *port, reg_t ID, rect_t zone, int font_nr, char **entries_list, + int entries_nr, int list_top, int selection, char inverse) +{ + gfxw_list_t *list; + + char arr_up[2], arr_down[2]; + int i; + + int font_height; + int columns; + + zone.x--; + zone.y--; + zone.xl++; + zone.yl++; + + list = gfxw_new_list(_move_and_extend_rect(zone, gfx_point(port->zone.x, port->zone.y), 1), 0); + + font_height = gfxop_get_font_height(port->visual->gfx_state, font_nr); + columns = (zone.yl - 20); + + if (font_height <= 0) { + GFXERROR("Attempt to create list control with invalid font %d\n", font_nr); + list->widfree(GFXW(list)); + return NULL; + } + + columns /= font_height; + + gfxw_set_id(GFXW(list), ID.segment, ID.offset); + + arr_up[0] = SCI_SPECIAL_CHAR_ARROW_UP; + arr_down[0] = SCI_SPECIAL_CHAR_ARROW_DOWN; + arr_up[1] = arr_down[1] = 0; + + zone.x = 1; + zone.y = 11; + + /* Draw text */ + + for (i = list_top; columns-- && i < entries_nr; i++) { + + if (i != selection) + list->add(GFXWC(list), + GFXW(gfxw_new_text(port->visual->gfx_state, gfx_rect(zone.x, zone.y, zone.xl - 2, font_height), + font_nr, entries_list[i], ALIGN_LEFT, ALIGN_TOP, + port->color, port->color, port->bgcolor, GFXR_FONT_FLAG_NO_NEWLINES))); + else { + list->add(GFXWC(list), GFXW(gfxw_new_box(port->visual->gfx_state, gfx_rect(zone.x, zone.y, zone.xl - 1, font_height), + port->color, port->color, GFX_BOX_SHADE_FLAT))); + list->add(GFXWC(list), + GFXW(gfxw_new_text(port->visual->gfx_state, gfx_rect(zone.x, zone.y, zone.xl - 2, font_height), + font_nr, entries_list[i], ALIGN_LEFT, ALIGN_TOP, + port->bgcolor, port->bgcolor, port->color, GFXR_FONT_FLAG_NO_NEWLINES))); + } + + zone.y += font_height; + } + + /* Draw frames */ + + zone.x = 0; + zone.y = 0; + + /* Add up arrow */ + list->add(GFXWC(list), + GFXW(gfxw_new_text(port->visual->gfx_state, gfx_rect(1, 0, zone.xl-2, 8), + port->font_nr, arr_up, ALIGN_CENTER, ALIGN_CENTER, + port->color, port->color, port->bgcolor, 0))); + + /* Add down arrow */ + list->add(GFXWC(list), + GFXW(gfxw_new_text(port->visual->gfx_state, gfx_rect(1, zone.yl-9, zone.xl-2, 8), + port->font_nr, arr_down, ALIGN_CENTER, ALIGN_CENTER, + port->color, port->color, port->bgcolor, 0))); + + if (list_top & 1) { /* Hack to work around aggressive caching */ + + list->add(GFXWC(list), + GFXW(gfxw_new_rect(zone, port->color, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL))); + + list->add(GFXWC(list), + GFXW(gfxw_new_rect(gfx_rect(zone.x, zone.y + 10, zone.xl, zone.yl - 20), + port->color, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL))); + } else { + + list->add(GFXWC(list), + GFXW(gfxw_new_rect(gfx_rect(zone.x, zone.y, zone.xl, zone.yl - 10), + port->color, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL))); + + list->add(GFXWC(list), + GFXW(gfxw_new_rect(gfx_rect(zone.x, zone.y + 10, zone.xl, zone.yl - 10), + port->color, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL))); + } + + return list; +} + + +void +sciw_set_menubar(state_t *s, gfxw_port_t *status_bar, menubar_t *menubar, int selection) +{ + gfxw_list_t *list = make_titlebar_list(s, status_bar->bounds, status_bar); + int offset = MENU_LEFT_BORDER; + int i; + + clear_titlebar(status_bar); + + for (i = 0; i < menubar->menus_nr; i++) { + menu_t *menu = menubar->menus + i; + int width = menu->title_width + (MENU_BORDER_SIZE * 2); + + if (i == selection) { + list->add(GFXWC(list), GFXW(gfxw_new_box(status_bar->visual->gfx_state, gfx_rect(offset, 0, width, MENU_BAR_HEIGHT), + status_bar->color, status_bar->color, GFX_BOX_SHADE_FLAT))); + list->add(GFXWC(list), + GFXW(gfxw_new_text(s->gfx_state, gfx_rect(offset, 0, width, MENU_BAR_HEIGHT), + status_bar->font_nr, menu->title, ALIGN_CENTER, ALIGN_CENTER, + status_bar->bgcolor, status_bar->bgcolor, status_bar->color, GFXR_FONT_FLAG_NO_NEWLINES))); + } else + list->add(GFXWC(list), + GFXW(gfxw_new_text(s->gfx_state, gfx_rect(offset, 0, width, MENU_BAR_HEIGHT), + status_bar->font_nr, menu->title, ALIGN_CENTER, ALIGN_CENTER, + status_bar->color, status_bar->color, status_bar->bgcolor, GFXR_FONT_FLAG_NO_NEWLINES))); + + offset += width; + } + + status_bar->add(GFXWC(status_bar), GFXW(list)); + finish_titlebar_list(s, list, status_bar); +} + +gfxw_port_t * +sciw_new_menu(state_t *s, gfxw_port_t *status_bar, menubar_t *menubar, int selection) +{ + gfxw_port_t *retval; + menu_t *menu = menubar->menus + selection; + rect_t area = gfx_rect(MENU_LEFT_BORDER, 10, 0, 0); + int i; + + if (selection < -1) + return NULL; + + if (selection >= menubar->menus_nr) { + GFXERROR("Attempt to make menu #%d of %d\n", selection, menubar->menus_nr); + return NULL; + } + + for (i = 0; i < selection; i++) + area.x += menubar->menus[i].title_width; + + area.xl = menu->width - 1; + area.yl = menu->items_nr * 10; + + retval = sciw_new_window(s, area, status_bar->font_nr, status_bar->color, status_bar->bgcolor, + 0, status_bar->color, status_bar->bgcolor, + NULL, WINDOW_FLAG_NO_DROP_SHADOW | WINDOW_FLAG_TRANSPARENT); + + retval->set_visual(GFXW(retval), s->visual); + + for (i = 0; i < menu->items_nr; i++) + sciw_unselect_item(s, retval, menu, i); + + return retval; +} + +#define MAGIC_ID_OFFSET 0x2000 + +static inline gfx_color_t +un_prioritize(gfx_color_t col) +{ + col.priority = -1; + col.mask &= ~GFX_MASK_PRIORITY; + + return col; +} + +gfxw_widget_t * +_make_menu_entry(menu_item_t *item, int offset, int width, gfxw_port_t *port, gfx_color_t color, gfx_color_t bgcolor, int ID, int gray) +{ + rect_t area = gfx_rect(MENU_BOX_LEFT_PADDING, 0, width - MENU_BOX_LEFT_PADDING, 10); + rect_t list_area = gfx_rect(port->zone.x, area.y + offset + port->zone.y, width, area.yl); + gfxw_list_t *list = (gfxw_list_t *) gfxw_set_id(GFXW(gfxw_new_list(list_area, 0)), ID, GFXW_NO_ID); + gfx_color_t xcolor = {0}; + + color = un_prioritize(color); + bgcolor = un_prioritize(bgcolor); + + xcolor = gray? color : bgcolor; + + list->add(GFXWC(list), GFXW(gfxw_new_box(port->visual->gfx_state, area, bgcolor, bgcolor, GFX_BOX_SHADE_FLAT))); + list->add(GFXWC(list), GFXW(gfxw_new_text(port->visual->gfx_state, area, port->font_nr, item->text, ALIGN_LEFT, ALIGN_CENTER, + color, xcolor, bgcolor, GFXR_FONT_FLAG_NO_NEWLINES))); + + if (item->keytext) { + area.xl -= MENU_BOX_RIGHT_PADDING; + list->add(GFXWC(list), GFXW(gfxw_new_text(port->visual->gfx_state, area, port->font_nr, item->keytext, ALIGN_RIGHT, ALIGN_CENTER, + color, xcolor, bgcolor, GFXR_FONT_FLAG_NO_NEWLINES))); + } + + return GFXW(list); +} + +gfxw_widget_t * +_make_menu_hbar(int offset, int width, gfxw_port_t *port, gfx_color_t color, gfx_color_t bgcolor, int ID) +{ + rect_t area = gfx_rect(0, 0, width, 10); + rect_t list_area = gfx_rect(area.x + port->zone.x, area.y + offset + port->zone.y, area.xl, area.yl); + gfxw_list_t *list = (gfxw_list_t *) gfxw_set_id(GFXW(gfxw_new_list(list_area, 0)), ID, GFXW_NO_ID); + + color = un_prioritize(color); + bgcolor = un_prioritize(bgcolor); + + list->add(GFXWC(list), GFXW(gfxw_new_box(port->visual->gfx_state, area, bgcolor, bgcolor, GFX_BOX_SHADE_FLAT))); + list->add(GFXWC(list), GFXW(gfxw_new_line(gfx_point(0, 5), + gfx_point(width, 5), + color, + GFX_LINE_MODE_FAST, GFX_LINE_STYLE_STIPPLED))); + + return GFXW(list); +} + +gfxw_port_t * +sciw_unselect_item(state_t *s, gfxw_port_t *menu_port, menu_t *menu, int selection) +{ + menu_item_t *item = menu->items + selection; + + if (selection < 0 || selection >= menu->items_nr) + return menu_port; + + if (item->type == MENU_TYPE_NORMAL) + menu_port->add(GFXWC(menu_port), GFXW(_make_menu_entry(item, selection * 10, menu_port->zone.xl + 1, + menu_port, menu_port->color, + menu_port->bgcolor, selection + MAGIC_ID_OFFSET, + item->enabled))); + else + menu_port->add(GFXWC(menu_port), GFXW(_make_menu_hbar(selection * 10, menu_port->zone.xl + 1, + menu_port, menu_port->color, + menu_port->bgcolor, selection + MAGIC_ID_OFFSET))); + + return menu_port; +} + +gfxw_port_t * +sciw_select_item(state_t *s, gfxw_port_t *menu_port, menu_t *menu, int selection) +{ + menu_item_t *item = menu->items + selection; + + if (selection < 0 || selection >= menu->items_nr) + return menu_port; + + if (item->type == MENU_TYPE_NORMAL) + menu_port->add(GFXWC(menu_port), GFXW(_make_menu_entry(item, selection * 10, menu_port->zone.xl + 1, + menu_port, menu_port->bgcolor, + menu_port->color, selection + MAGIC_ID_OFFSET, + item->enabled))); + else + menu_port->add(GFXWC(menu_port), GFXW(_make_menu_hbar(selection * 10, menu_port->zone.xl + 1, + menu_port, menu_port->bgcolor, + menu_port->color, selection + MAGIC_ID_OFFSET))); + + return menu_port; +} diff --git a/engines/sci/gfx/widgets.c b/engines/sci/gfx/widgets.c new file mode 100644 index 0000000000..f81a717f21 --- /dev/null +++ b/engines/sci/gfx/widgets.c @@ -0,0 +1,2600 @@ +/*************************************************************************** + widgets.c Copyright (C) 2000,01 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include + +#undef GFXW_DEBUG_DIRTY /* Enable to debug dirty rectangle propagation (writes to stderr) */ + +#ifdef GFXW_DEBUG_DIRTY +# define DDIRTY fprintf(stderr, "%s:%5d| ", __FILE__, __LINE__); fprintf +#else +# define DDIRTY if (0) fprintf +#endif + +point_t gfxw_point_zero = {0, 0}; + +#define MAX_SERIAL_NUMBER 0x7fffffff +static int widget_serial_number_counter = 0x10000; /* Avoid confusion with IDs */ + +#ifdef GFXW_DEBUG_WIDGETS + +gfxw_widget_t *debug_widgets[GFXW_DEBUG_WIDGETS]; +int debug_widget_pos = 0; + +static void +_gfxw_debug_add_widget(gfxw_widget_t *widget) +{ + if (debug_widget_pos == GFXW_DEBUG_WIDGETS) { + GFXERROR("WIDGET DEBUG: Allocated the maximum number of %d widgets- Aborting!\n", GFXW_DEBUG_WIDGETS); + BREAKPOINT(); + } + debug_widgets[debug_widget_pos++] = widget; +} + +static void +_gfxw_debug_remove_widget(gfxw_widget_t *widget) { + int i; + int found = 0; + for (i = 0; i < debug_widget_pos; i++) { + if (debug_widgets[i] == widget) { + memmove(debug_widgets + i, debug_widgets + i + 1, + (sizeof (gfxw_widget_t *)) * (debug_widget_pos - i - 1)); + debug_widgets[debug_widget_pos--] = NULL; + found++; + } + } + + if (found > 1) { + GFXERROR("While removing widget: Found it %d times!\n", found); + BREAKPOINT(); + } + + if (found == 0) { + GFXERROR("Attempted removal of unregistered widget!\n"); + BREAKPOINT(); + } +} +#else /* !GFXW_DEBUG_WIDGETS */ +#define _gfxw_debug_add_widget(a) +#define _gfxw_debug_remove_widget(a) +#endif + + +static inline void +indent(int indentation) +{ + int i; + for (i = 0; i < indentation; i++) + sciprintf(" "); +} + +static void +_gfxw_print_widget(gfxw_widget_t *widget, int indentation) +{ + unsigned int i; + char flags_list[] = "VOCDTMI"; + + indent(indentation); + + if (widget->magic == GFXW_MAGIC_VALID) { + if (widget->visual) + sciprintf("v "); + else + sciprintf("NoVis "); + } else if (widget->magic == GFXW_MAGIC_INVALID) + sciprintf("INVALID "); + + sciprintf("S%08x", widget->serial); + + if (widget->ID != GFXW_NO_ID) { + sciprintf("#%x", widget->ID); + + if (widget->subID != GFXW_NO_ID) + sciprintf(":%x ", widget->subID); + else + sciprintf(" "); + } + + sciprintf("[(%d,%d)(%dx%d)]", widget->bounds.x, widget->bounds.y, widget->bounds.xl, widget->bounds.yl); + + for (i = 0; i < strlen(flags_list); i++) + if (widget->flags & (1 << i)) + sciprintf("%c", flags_list[i]); + + sciprintf(" "); +} + +static int +_gfxwop_print_empty(gfxw_widget_t *widget, int indentation) +{ + _gfxw_print_widget(widget, indentation); + sciprintf("", widget->type); + + return 0; +} + + +gfxw_widget_t * +_gfxw_new_widget(int size, gfxw_widget_type_t type) +{ + gfxw_widget_t *widget = (gfxw_widget_t*)sci_malloc(size); +#ifdef SATISFY_PURIFY + memset(widget, 0, size); +#endif + + widget->magic = GFXW_MAGIC_VALID; + widget->parent = NULL; + widget->visual = NULL; + widget->next = NULL; + widget->type = type; + widget->bounds = gfx_rect(0, 0, 0, 0); + widget->flags = GFXW_FLAG_DIRTY; + widget->ID = GFXW_NO_ID; + widget->subID = GFXW_NO_ID; + widget->serial = widget_serial_number_counter++; + widget->widget_priority = -1; + + widget_serial_number_counter &= MAX_SERIAL_NUMBER; + + widget->draw = NULL; + widget->widfree = NULL; + widget->tag = NULL; + widget->print = _gfxwop_print_empty; + widget->should_replace = NULL; + widget->compare_to = widget->equals = widget->superarea_of = NULL; + + _gfxw_debug_add_widget(widget); + + return widget; +} + + +static inline int +verify_widget(gfxw_widget_t *widget) +{ + if (!widget) { + GFXERROR("Attempt to use NULL widget\n"); +#ifdef GFXW_DEBUG_WIDGETS + BREAKPOINT(); +#endif /* GFXW_DEBUG_WIDGETS */ + return 1; + } else if (widget->magic != GFXW_MAGIC_VALID) { + if (widget->magic == GFXW_MAGIC_INVALID) { + GFXERROR("Attempt to use invalidated widget\n"); + } else { + GFXERROR("Attempt to use non-widget\n"); + } +#ifdef GFXW_DEBUG_WIDGETS + BREAKPOINT(); +#endif /* GFXW_DEBUG_WIDGETS */ + return 1; + } + return 0; +} + +#define VERIFY_WIDGET(w) \ + if (verify_widget((gfxw_widget_t *)(w))) { GFXERROR("Error occured while validating widget\n"); } + +static void +_gfxw_unallocate_widget(gfx_state_t *state, gfxw_widget_t *widget) +{ + if (GFXW_IS_TEXT(widget)) { + gfxw_text_t *text = (gfxw_text_t *) widget; + + if (text->text_handle) { + if (!state) { + GFXERROR("Attempt to free text without supplying mode to free it from!\n"); + BREAKPOINT(); + } else { + gfxop_free_text(state, text->text_handle); + text->text_handle = NULL; + } + } + } + + widget->magic = GFXW_MAGIC_INVALID; + free(widget); + _gfxw_debug_remove_widget(widget); +} + +#define GFX_ASSERT(_x) \ + { \ + int retval = (_x); \ + if (retval == GFX_ERROR) { \ + GFXERROR("Error occured while drawing widget!\n"); \ + return 1; \ + } else if (retval == GFX_FATAL) { \ + GFXERROR("Fatal error occured while drawing widget!\nGraphics state invalid; aborting program..."); \ + exit(1); \ + } \ + } + + +/**********************************/ +/*********** Widgets **************/ +/**********************************/ + +/* Base class operations and common stuff */ + +/* Assertion for drawing */ +#define DRAW_ASSERT(widget, exp_type) \ + if (!(widget)) { \ + sciprintf("L%d: NULL widget!\n", __LINE__); \ + return 1; \ + } \ + if (!(widget)->print) { \ + sciprintf("L%d: Widget of type %d does not have print function!\n", __LINE__, \ + (widget)->type); \ + } \ + if ((widget)->type != (exp_type)) { \ + sciprintf("L%d: Error in widget: Expected type " # exp_type "(%d) but got %d\n", \ + __LINE__, exp_type, (widget)->type); \ + sciprintf("Erroneous widget: "); \ + widget->print(widget, 4); \ + sciprintf("\n"); \ + return 1; \ + } \ + if (!(widget->flags & GFXW_FLAG_VISIBLE)) \ + return 0; \ + if (!(widget->type == GFXW_VISUAL || widget->visual)) { \ + sciprintf("L%d: Error while drawing widget: Widget has no visual\n", __LINE__); \ + sciprintf("Erroneous widget: "); \ + widget->print(widget, 1); \ + sciprintf("\n"); \ + return 1; \ + } + + +static inline int +_color_equals(gfx_color_t a, gfx_color_t b) +{ + if (a.mask != b.mask) + return 0; + + if (a.mask & GFX_MASK_VISUAL) { + if (a.visual.r != b.visual.r + || a.visual.g != b.visual.g + || a.visual.b != b.visual.b + || a.alpha != b.alpha) + return 0; + } + + if (a.mask & GFX_MASK_PRIORITY) + if (a.priority != b.priority) + return 0; + + if (a.mask & GFX_MASK_CONTROL) + if (a.control != b.control) + return 0; + + return 1; +} + +static int +_gfxwop_basic_set_visual(gfxw_widget_t *widget, gfxw_visual_t *visual) +{ + widget->visual = visual; + + if (widget->parent) { + DDIRTY(stderr,"basic_set_visual: DOWNWARDS rel(%d,%d,%d,%d, 1)\n", + GFX_PRINT_RECT(widget->bounds)); + widget->parent->add_dirty_rel(widget->parent, widget->bounds, 1); + } + + return 0; +} + + +static int +_gfxwop_basic_should_replace(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + return 0; +} + +static inline void +_gfxw_set_ops(gfxw_widget_t *widget, gfxw_point_op *draw, gfxw_op *free, gfxw_op *tag, gfxw_op_int *print, + gfxw_bin_op *compare_to, gfxw_bin_op *equals, gfxw_bin_op *superarea_of) +{ + widget->draw = draw; + widget->widfree = free; + widget->tag = tag; + widget->print = print; + widget->compare_to = compare_to; + widget->equals = equals; + widget->superarea_of = superarea_of; + + widget->should_replace = _gfxwop_basic_should_replace; + widget->set_visual = _gfxwop_basic_set_visual; +} + +void +gfxw_remove_widget_from_container(gfxw_container_t *container, gfxw_widget_t *widget) +{ + gfxw_widget_t **seekerp; + + if (!container) { + GFXERROR("Attempt to remove widget from NULL container!\n"); + BREAKPOINT(); + } + + seekerp = &(container->contents); + + if (GFXW_IS_LIST(widget) && GFXW_IS_PORT(container)) { + gfxw_port_t *port = (gfxw_port_t *) container; + if (port->decorations == (gfxw_list_t *) widget) { + port->decorations = NULL; + return; + } + } + + while (*seekerp && *seekerp != widget) + seekerp = &((*seekerp)->next); + + if (!*seekerp) { + GFXERROR("Internal error: Attempt to remove widget from container it was not contained in!\n"); + sciprintf("Widget:"); + widget->print(GFXW(widget), 1); + sciprintf("Container:"); + widget->print(GFXW(container), 1); + BREAKPOINT(); + return; + } + + if (container->nextpp == &(widget->next)) + container->nextpp = seekerp; + + *seekerp = widget->next; /* Remove it */ + widget->parent = NULL; + widget->next = NULL; +} + +static int +_gfxwop_basic_free(gfxw_widget_t *widget) +{ + gfxw_visual_t *visual = widget->visual; + gfx_state_t *state = (visual)? visual->gfx_state : NULL; + + DDIRTY(stderr, "BASIC-FREE: SomeAddDirty\n"); + + if (widget->parent) { + if (GFXW_IS_CONTAINER(widget)) + widget->parent->add_dirty_abs(widget->parent, widget->bounds, 1); + else + widget->parent->add_dirty_rel(widget->parent, widget->bounds, 1); + + gfxw_remove_widget_from_container(widget->parent, widget); + } + + _gfxw_unallocate_widget(state, widget); + + + return 0; +} + + +static int +_gfxwop_basic_tag(gfxw_widget_t *widget) +{ + widget->flags |= GFXW_FLAG_TAGGED; + + return 0; +} + + +static int +_gfxwop_basic_compare_to(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + return 1; +} + + +static int +_gfxwop_basic_equals(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + return 0; +} + + +static int +_gfxwop_basic_superarea_of(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + return (widget == other); +} + +/*-------------*/ +/**** Boxes ****/ +/*-------------*/ + +static inline rect_t +_move_rect(rect_t rect, point_t point) +{ + return gfx_rect(rect.x + point.x, rect.y + point.y, rect.xl, rect.yl); +} + +static inline void +_split_rect(rect_t rect, point_t *p1, point_t *p2) +{ + p1->x = rect.x; + p1->y = rect.y; + p2->x = rect.x + rect.xl; + p2->y = rect.y + rect.yl; +} + +static inline point_t +_move_point(rect_t rect, point_t point) +{ + return gfx_point(rect.x + point.x, rect.y + point.y); +} + +static int +_gfxwop_box_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_box_t *box = (gfxw_box_t *) widget; + DRAW_ASSERT(widget, GFXW_BOX); + GFX_ASSERT(gfxop_draw_box(box->visual->gfx_state, _move_rect(box->bounds, pos), box->color1, + box->color2, box->shade_type)); + + return 0; +} + +static int +_gfxwop_box_print(gfxw_widget_t *widget, int indentation) +{ + _gfxw_print_widget(widget, indentation); + sciprintf("BOX"); + return 0; +} + +static int +_gfxwop_box_superarea_of(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + gfxw_box_t *box = (gfxw_box_t *) widget; + + if (box->color1.alpha) + return 0; + + if (box->shade_type != GFX_BOX_SHADE_FLAT && box->color2.alpha) + return 0; + + if (!gfx_rect_subset(other->bounds, box->bounds)) + return 0; + + return 1; +} + +static int +_gfxwop_box_equals(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + gfxw_box_t *wbox = (gfxw_box_t *) widget, *obox; + if (other->type != GFXW_BOX) + return 0; + + obox = (gfxw_box_t *) other; + + if (!gfx_rect_equals(wbox->bounds, obox->bounds)) + return 0; + + if (!_color_equals(wbox->color1, obox->color1)) + return 0; + + if (wbox->shade_type != obox->shade_type) + return 0; + + if (wbox->shade_type != GFX_BOX_SHADE_FLAT + && _color_equals(wbox->color2, obox->color2)) + return 0; + + return 1; +} + +void +_gfxw_set_ops_BOX(gfxw_widget_t *widget) +{ + _gfxw_set_ops(GFXW(widget), _gfxwop_box_draw, + _gfxwop_basic_free, + _gfxwop_basic_tag, + _gfxwop_box_print, + _gfxwop_basic_compare_to, + _gfxwop_box_equals, + _gfxwop_box_superarea_of); +} + +static inline int +_gfxw_color_get_priority(gfx_color_t color) +{ + return (color.mask & GFX_MASK_PRIORITY)? color.priority : -1; +} + +gfxw_box_t * +gfxw_new_box(gfx_state_t *state, rect_t area, gfx_color_t color1, gfx_color_t color2, gfx_box_shade_t shade_type) +{ + gfxw_box_t *widget = (gfxw_box_t *) _gfxw_new_widget(sizeof(gfxw_box_t), GFXW_BOX); + + widget->widget_priority = _gfxw_color_get_priority(color1); + widget->bounds = area; + widget->color1 = color1; + widget->color2 = color2; + widget->shade_type = shade_type; + + widget->flags |= GFXW_FLAG_VISIBLE; + + if ((color1.mask & GFX_MASK_VISUAL) + && ((state && (state->driver->mode->palette)) + || (!color1.alpha && !color2.alpha))) + widget->flags |= GFXW_FLAG_OPAQUE; + + _gfxw_set_ops_BOX(GFXW(widget)); + + return widget; +} + + +static inline gfxw_primitive_t * +_gfxw_new_primitive(rect_t area, gfx_color_t color, gfx_line_mode_t mode, + gfx_line_style_t style, gfxw_widget_type_t type) +{ + gfxw_primitive_t *widget = (gfxw_primitive_t *) _gfxw_new_widget(sizeof(gfxw_primitive_t), type); + + widget->widget_priority = _gfxw_color_get_priority(color); + widget->bounds = area; + widget->color = color; + widget->line_mode = mode; + widget->line_style = style; + + widget->flags |= GFXW_FLAG_VISIBLE; + return widget; +} + +/*------------------*/ +/**** Rectangles ****/ +/*------------------*/ + +static int +_gfxwop_primitive_equals(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + gfxw_primitive_t *wprim = (gfxw_primitive_t *) widget, *oprim; + if (widget->type != other->type) + return 0; + + oprim = (gfxw_primitive_t *) other; + + if (!gfx_rect_equals(wprim->bounds, oprim->bounds)) + return 0; + + if (!_color_equals(wprim->color, oprim->color)) + return 0; + + if (wprim->line_mode != oprim->line_mode) + return 0; + + if (wprim->line_style != oprim->line_style) + return 0; + + return 1; +} + +static int +_gfxwop_rect_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_primitive_t *rect = (gfxw_primitive_t *) widget; + DRAW_ASSERT(widget, GFXW_RECT); + + GFX_ASSERT(gfxop_draw_rectangle(rect->visual->gfx_state, + gfx_rect(rect->bounds.x + pos.x, rect->bounds.y + pos.y, + rect->bounds.xl - 1, rect->bounds.yl - 1), + rect->color, rect->line_mode, rect->line_style)); + + return 0; +} + +static int +_gfxwop_rect_print(gfxw_widget_t *rect, int indentation) +{ + _gfxw_print_widget(GFXW(rect), indentation); + sciprintf("RECT"); + return 0; +} + + +void +_gfxw_set_ops_RECT(gfxw_widget_t *prim) +{ + _gfxw_set_ops(GFXW(prim), _gfxwop_rect_draw, + _gfxwop_basic_free, + _gfxwop_basic_tag, + _gfxwop_rect_print, + _gfxwop_basic_compare_to, + _gfxwop_primitive_equals, + _gfxwop_basic_superarea_of); +} + +gfxw_primitive_t * +gfxw_new_rect(rect_t rect, gfx_color_t color, gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + gfxw_primitive_t *prim = _gfxw_new_primitive(rect, color, line_mode, line_style, GFXW_RECT); + prim->bounds.xl++; + prim->bounds.yl++; /* Since it is actually one pixel bigger in each direction */ + + _gfxw_set_ops_RECT(GFXW(prim)); + + return prim; +} + + +/*-------------*/ +/**** Lines ****/ +/*-------------*/ + +static int +_gfxwop_line_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_primitive_t *line = (gfxw_primitive_t *) widget; + rect_t linepos = widget->bounds; + point_t p1, p2; + + linepos.xl--; + linepos.yl--; + + if (widget->type == GFXW_INVERSE_LINE) { + linepos.x += linepos.xl; + linepos.xl = -linepos.xl; + } else { + DRAW_ASSERT(widget, GFXW_LINE); + } + + + _split_rect(_move_rect(linepos, pos), &p1, &p2); + GFX_ASSERT(gfxop_draw_line(line->visual->gfx_state, p1, p2, + line->color, line->line_mode, line->line_style)); + + return 0; +} + +static int +_gfxwop_line_print(gfxw_widget_t *widget, int indentation) +{ + _gfxw_print_widget(widget, indentation); + if (widget->type == GFXW_INVERSE_LINE) + sciprintf("INVERSE-LINE"); + else + sciprintf("LINE"); + return 0; +} + +void +_gfxw_set_ops_LINE(gfxw_widget_t *prim) +{ + _gfxw_set_ops(GFXW(prim), _gfxwop_line_draw, + _gfxwop_basic_free, + _gfxwop_basic_tag, + _gfxwop_line_print, + _gfxwop_basic_compare_to, + _gfxwop_primitive_equals, + _gfxwop_basic_superarea_of); + +} + +gfxw_primitive_t * +gfxw_new_line(point_t start, point_t end, gfx_color_t color, gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + gfxw_primitive_t *prim; + /* Encode into internal representation */ + rect_t line = gfx_rect (start.x, start.y, end.x - start.x, end.y - start.y); + + byte inverse = 0; + + if (line.xl < 0) { + line.x += line.xl; + line.y += line.yl; + line.xl = -line.xl; + line.yl = -line.yl; + } + + if (line.yl < 0) { + inverse = 1; + line.x += line.xl; + line.xl = -line.xl; + } + + line.xl++; + line.yl++; + + prim = _gfxw_new_primitive(line, color, line_mode, line_style, inverse? GFXW_INVERSE_LINE : GFXW_LINE); + + _gfxw_set_ops_LINE(GFXW(prim)); + + return prim; +} + +/*------------------------------*/ +/**** Views and static views ****/ +/*------------------------------*/ + + +gfxw_view_t * +_gfxw_new_simple_view(gfx_state_t *state, point_t pos, int view, int loop, int cel, int palette, int priority, int control, + gfx_alignment_t halign, gfx_alignment_t valign, int size, gfxw_widget_type_t type) +{ + gfxw_view_t *widget; + int width, height; + point_t offset; + + if (!state) { + GFXERROR("Attempt to create view widget with NULL state!\n"); + return NULL; + } + + if (gfxop_get_cel_parameters(state, view, loop, cel, &width, &height, &offset)) { + GFXERROR("Attempt to retreive cel parameters for (%d/%d/%d) failed (Maybe the values weren't checked beforehand?)\n", + view, cel, loop); + return NULL; + } + + widget = (gfxw_view_t *) _gfxw_new_widget(size, type); + + widget->widget_priority = priority; + widget->pos = pos; + widget->color.mask = + ((priority < 0)? 0 : GFX_MASK_PRIORITY) + | ((control < 0)? 0 : GFX_MASK_CONTROL); + widget->color.priority = priority; + widget->color.control = control; + widget->view = view; + widget->loop = loop; + widget->cel = cel; + widget->palette = palette; + + if (halign == ALIGN_CENTER) + widget->pos.x -= width >> 1; + else if (halign == ALIGN_RIGHT) + widget->pos.x -= width; + + if (valign == ALIGN_CENTER) + widget->pos.y -= height >> 1; + else if (valign == ALIGN_BOTTOM) + widget->pos.y -= height; + + widget->bounds = gfx_rect(widget->pos.x - offset.x, widget->pos.y - offset.y, width, height); + + widget->flags |= GFXW_FLAG_VISIBLE; + + return widget; +} + +int +_gfxwop_view_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_view_t *view = (gfxw_view_t *) widget; + DRAW_ASSERT(widget, GFXW_VIEW); + + GFX_ASSERT(gfxop_draw_cel(view->visual->gfx_state, view->view, view->loop, + view->cel, gfx_point(view->pos.x + pos.x, view->pos.y + pos.y), + view->color, view->palette)); + + return 0; +} + +static int +_gfxwop_static_view_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_view_t *view = (gfxw_view_t *) widget; + DRAW_ASSERT(widget, GFXW_VIEW); + + GFX_ASSERT(gfxop_draw_cel_static(view->visual->gfx_state, view->view, view->loop, + view->cel, _move_point(view->bounds, pos), + view->color, view->palette)); + + return 0; +} + +static int +_w_gfxwop_view_print(gfxw_widget_t *widget, const char *name, int indentation) +{ + gfxw_view_t *view = (gfxw_view_t *) widget; + _gfxw_print_widget(widget, indentation); + + sciprintf(name); + sciprintf("(%d/%d/%d)@(%d,%d)[p:%d,c:%d]", view->view, view->loop, view->cel, view->pos.x, view->pos.y, + (view->color.mask & GFX_MASK_PRIORITY)? view->color.priority : -1, + (view->color.mask & GFX_MASK_CONTROL)? view->color.control : -1); + + return 0; +} + +static int +_gfxwop_view_print(gfxw_widget_t *widget, int indentation) +{ + return _w_gfxwop_view_print(widget, "VIEW", indentation); +} + +static int +_gfxwop_static_view_print(gfxw_widget_t *widget, int indentation) +{ + return _w_gfxwop_view_print(widget, "PICVIEW", indentation); +} + +void +_gfxw_set_ops_VIEW(gfxw_widget_t *view, char stat) +{ + _gfxw_set_ops(GFXW(view), (stat) ? _gfxwop_static_view_draw : _gfxwop_view_draw, + _gfxwop_basic_free, + _gfxwop_basic_tag, + (stat) ? _gfxwop_static_view_print : _gfxwop_view_print, + _gfxwop_basic_compare_to, + _gfxwop_basic_equals, + _gfxwop_basic_superarea_of); +} + +gfxw_view_t * +gfxw_new_view(gfx_state_t *state, point_t pos, int view_nr, int loop, int cel, int palette, int priority, int control, + gfx_alignment_t halign, gfx_alignment_t valign, int flags) +{ + gfxw_view_t *view; + + if (flags & GFXW_VIEW_FLAG_DONT_MODIFY_OFFSET) { + int foo; + point_t offset; + gfxop_get_cel_parameters(state, view_nr, loop, cel, &foo, &foo, &offset); + pos.x += offset.x; + pos.y += offset.y; + } + + view = _gfxw_new_simple_view(state, pos, view_nr, loop, cel, palette, priority, control, halign, valign, + sizeof(gfxw_view_t), (flags & GFXW_VIEW_FLAG_STATIC) ? GFXW_STATIC_VIEW : GFXW_VIEW); + + _gfxw_set_ops_VIEW(GFXW(view), (char)(flags & GFXW_VIEW_FLAG_STATIC)); + + return view; +} + +/*---------------------*/ +/**** Dynamic Views ****/ +/*---------------------*/ + +static int +_gfxwop_dyn_view_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_dyn_view_t *view = (gfxw_dyn_view_t *) widget; + DRAW_ASSERT(widget, GFXW_DYN_VIEW); + + GFX_ASSERT(gfxop_draw_cel(view->visual->gfx_state, view->view, view->loop, + view->cel, _move_point(view->draw_bounds, pos), + view->color, view->palette)); + + /* + gfx_color_t red; red.visual.r = 0xff; red.visual.g = red.visual.b = 0; red.mask = GFX_MASK_VISUAL; + GFX_ASSERT(gfxop_draw_rectangle(view->visual->gfx_state, + gfx_rect(view->bounds.x + pos.x, view->bounds.y + pos.y, + view->bounds.xl - 1, view->bounds.yl - 1), + red, 0, 0)); + */ + + + return 0; + +} + +static int +_gfxwop_draw_nop(gfxw_widget_t *widget, point_t pos) +{ + return 0; +} + +static int +_gfxwop_pic_view_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_dyn_view_t *view = (gfxw_dyn_view_t *) widget; + DRAW_ASSERT(widget, GFXW_PIC_VIEW); + + GFX_ASSERT(gfxop_set_clip_zone(view->visual->gfx_state, view->parent->zone)); + GFX_ASSERT(gfxop_draw_cel_static_clipped(view->visual->gfx_state, + view->view, view->loop, + view->cel, + _move_point(view->draw_bounds, pos), + view->color, view->palette)); + + /* Draw again on the back buffer */ + GFX_ASSERT(gfxop_draw_cel(view->visual->gfx_state, + view->view, view->loop, + view->cel, + _move_point(view->draw_bounds, pos), + view->color, view->palette)); + + + widget->draw = _gfxwop_draw_nop; /* No more drawing needs to be done */ + + + return 0; +} + +static int +_gfxwop_some_view_print(gfxw_widget_t *widget, int indentation, const char *type_string) +{ + gfxw_dyn_view_t *view = (gfxw_dyn_view_t *) widget; + + _gfxw_print_widget(widget, indentation); + + sciprintf(type_string); + sciprintf(" SORT=%d z=%d seq=%d (%d/%d/%d)@(%d,%d)[p:%d,c:%d]; sig[%04x@%04x]", view->force_precedence, view->z, + view->sequence, view->view, view->loop, view->cel, view->pos.x, view->pos.y, + (view->color.mask & GFX_MASK_PRIORITY)? view->color.priority : -1, + (view->color.mask & GFX_MASK_CONTROL)? view->color.control : -1, + view->signal, view->signalp); + + return 0; +} + +static int +_gfxwop_dyn_view_print(gfxw_widget_t *widget, int indentation) +{ + return _gfxwop_some_view_print(widget, indentation, "DYNVIEW"); +} + +static int +_gfxwop_pic_view_print(gfxw_widget_t *widget, int indentation) +{ + return _gfxwop_some_view_print(widget, indentation, "PICVIEW"); +} + + +static int +_gfxwop_dyn_view_equals(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + gfxw_dyn_view_t *wview = (gfxw_dyn_view_t *) widget, *oview; + if (!GFXW_IS_DYN_VIEW(other)) + return 0; + + oview = (gfxw_dyn_view_t *) other; + + if (wview->pos.x != oview->pos.x + || wview->pos.y != oview->pos.y + || wview->z != oview->z) + return 0; + + if (wview->view != oview->view + || wview->loop != oview->loop + || wview->cel != oview->cel) + return 0; + + if (!_color_equals(wview->color, oview->color)) + return 0; + + if (wview->flags != oview->flags) + return 0; + + return 1; +} + +static int +_gfxwop_dyn_view_compare_to(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + int retval; + gfxw_dyn_view_t *wview = (gfxw_dyn_view_t *) widget, *oview; + if (!GFXW_IS_DYN_VIEW(other)) + return 1; + + oview = (gfxw_dyn_view_t *) other; + + retval = wview->force_precedence - oview->force_precedence; + if (retval) + return retval; + + retval = wview->pos.y - oview->pos.y; + if (retval) + return retval; + + retval = (wview->z - oview->z); + if (retval) + return retval; + + return -(wview->sequence - oview->sequence); +} + + +void +_gfxw_set_ops_DYNVIEW(gfxw_widget_t *widget) +{ + _gfxw_set_ops(GFXW(widget), _gfxwop_dyn_view_draw, + _gfxwop_basic_free, + _gfxwop_basic_tag, + _gfxwop_dyn_view_print, + _gfxwop_dyn_view_compare_to, + _gfxwop_dyn_view_equals, + _gfxwop_basic_superarea_of); +} + +void +_gfxw_set_ops_PICVIEW(gfxw_widget_t *widget) +{ + _gfxw_set_ops_DYNVIEW(widget); + widget->draw = _gfxwop_pic_view_draw; + widget->print = _gfxwop_pic_view_print; +} + +gfxw_dyn_view_t * +gfxw_new_dyn_view(gfx_state_t *state, point_t pos, int z, int view, int loop, int cel, int palette, int priority, int control, + gfx_alignment_t halign, gfx_alignment_t valign, int sequence) +{ + gfxw_dyn_view_t *widget; + int width, height; + int xalignmod, yalignmod; + point_t offset; + + if (!state) { + GFXERROR("Attempt to create view widget with NULL state!\n"); + return NULL; + } + + if (gfxop_get_cel_parameters(state, view, loop, cel, &width, &height, &offset)) { + GFXERROR("Attempt to retreive cel parameters for (%d/%d/%d) failed (Maybe the values weren't checked beforehand?)\n", + view, cel, loop); + return NULL; + } + + widget = (gfxw_dyn_view_t *) _gfxw_new_widget(sizeof(gfxw_dyn_view_t), GFXW_DYN_VIEW); + + widget->pos = pos; + widget->color.mask = + ((priority < 0)? 0 : GFX_MASK_PRIORITY) + | ((control < 0)? 0 : GFX_MASK_CONTROL); + widget->widget_priority = priority; + widget->color.priority = priority; + widget->color.control = control; + widget->color.alpha = 0; + widget->color.visual.global_index = 0; + widget->color.visual.r = 0; + widget->color.visual.g = 0; + widget->color.visual.b = 0; + widget->view = view; + widget->loop = loop; + widget->cel = cel; + widget->sequence = sequence; + widget->force_precedence = 0; + widget->palette = palette; + + if (halign == ALIGN_CENTER) + xalignmod = width >> 1; + else if (halign == ALIGN_RIGHT) + xalignmod = width; + else + xalignmod = 0; + + if (valign == ALIGN_CENTER) + yalignmod = height >> 1; + else if (valign == ALIGN_BOTTOM) + yalignmod = height; + else + yalignmod = 0; + + widget->z = z; + + widget->draw_bounds = gfx_rect(widget->pos.x - xalignmod, + widget->pos.y - yalignmod - z, width, height); + widget->bounds = gfx_rect(widget->pos.x - offset.x - xalignmod, + widget->pos.y - offset.y - yalignmod - z, width, height); + + widget->flags |= GFXW_FLAG_VISIBLE; + + _gfxw_set_ops_DYNVIEW(GFXW(widget)); + + widget->signalp = NULL; + widget->signal = 0; + + return widget; +} + +/*------------*/ +/**** Text ****/ +/*------------*/ + +static int +_gfxwop_text_free(gfxw_widget_t *widget) +{ + gfxw_text_t *text = (gfxw_text_t *) widget; + free(text->text); + return _gfxwop_basic_free(widget); +} + +static int +_gfxwop_text_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_text_t *text = (gfxw_text_t *) widget; + DRAW_ASSERT(widget, GFXW_TEXT); + + GFX_ASSERT(gfxop_draw_text(text->visual->gfx_state, text->text_handle, _move_rect(text->bounds, pos))); + + return 0; +} + +static int +_gfxwop_text_alloc_and_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_text_t *text = (gfxw_text_t *) widget; + DRAW_ASSERT(widget, GFXW_TEXT); + + text->text_handle = + gfxop_new_text(widget->visual->gfx_state, text->font_nr, text->text, text->bounds.xl, + text->halign, text->valign, text->color1, + text->color2, text->bgcolor, text->text_flags); + + text->draw = _gfxwop_text_draw; + + return _gfxwop_text_draw(widget, pos); +} + + +static int +_gfxwop_text_print(gfxw_widget_t *widget, int indentation) +{ + _gfxw_print_widget(widget, indentation); + sciprintf("TEXT:'%s'", ((gfxw_text_t *)widget)->text); + return 0; +} + + +static int +_gfxwop_text_equals(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + gfxw_text_t *wtext = (gfxw_text_t *) widget, *otext; + if (other->type != GFXW_TEXT) + return 0; + + otext = (gfxw_text_t *) other; + + if ((wtext->bounds.x != otext->bounds.x) + || (wtext->bounds.y != otext->bounds.y)) + return 0; + + if (wtext->halign != otext->halign + || wtext->valign != otext->valign) + return 0; + + if (wtext->text_flags != otext->text_flags) + return 0; + + if (wtext->font_nr != otext->font_nr) + return 0; + + /* if (!(_color_equals(wtext->color1, otext->color1) + && _color_equals(wtext->color2, otext->color2) + && _color_equals(wtext->bgcolor, otext->bgcolor))) + return 0; */ + + return 1; +} + +static int +_gfxwop_text_should_replace(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + gfxw_text_t *wtext = (gfxw_text_t *) widget, *otext; + + if (other->type != GFXW_TEXT) + return 0; + + otext = (gfxw_text_t *) other; + + return strcmp(wtext->text, otext->text); +} + + +static int +_gfxwop_text_compare_to(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + + return 1; +} + +void +_gfxw_set_ops_TEXT(gfxw_widget_t *widget) +{ + _gfxw_set_ops(GFXW(widget), _gfxwop_text_alloc_and_draw, + _gfxwop_text_free, + _gfxwop_basic_tag, + _gfxwop_text_print, + _gfxwop_text_compare_to, + _gfxwop_text_equals, + _gfxwop_basic_superarea_of); + widget->should_replace = _gfxwop_text_should_replace; +} + +gfxw_text_t * +gfxw_new_text(gfx_state_t *state, rect_t area, int font, const char *text, gfx_alignment_t halign, + gfx_alignment_t valign, gfx_color_t color1, gfx_color_t color2, + gfx_color_t bgcolor, int text_flags) +{ + gfxw_text_t *widget = (gfxw_text_t *) + _gfxw_new_widget(sizeof(gfxw_text_t), GFXW_TEXT); + + widget->widget_priority = _gfxw_color_get_priority(color1); + widget->font_nr = font; + widget->text = (char*)sci_malloc(strlen(text) + 1); + widget->halign = halign; + widget->valign = valign; + widget->color1 = color1; + widget->color2 = color2; + widget->bgcolor = bgcolor; + widget->text_flags = text_flags; + widget->text_handle = NULL; + + strcpy(widget->text, text); + + gfxop_get_text_params(state, font, text, area.xl, &(widget->width), + &(widget->height), text_flags, + &(widget->lines_nr), &(widget->lineheight), + &(widget->lastline_width)); + + /* FIXME: Window is too big + area.x += _calc_needmove(halign, area.xl, widget->width); + area.y += _calc_needmove(valign, area.yl, widget->height); + */ + + if (halign == ALIGN_LEFT) + area.xl = widget->width; + if (valign == ALIGN_TOP) + area.yl = widget->height; + + widget->bounds = area; + + widget->flags |= GFXW_FLAG_VISIBLE; + + _gfxw_set_ops_TEXT(GFXW(widget)); + + return widget; +} + + +void +gfxw_text_info(gfx_state_t *state, gfxw_text_t *text, int *lines, + int *lineheight, int *offset) +{ + if (lines) + *lines = text->lines_nr; + if (lineheight) + *lineheight = text->lineheight; + if (offset) + *offset = text->lastline_width; +} + + +/***********************/ +/*-- Container types --*/ +/***********************/ + +static int +_gfxwop_container_add_dirty_rel(gfxw_container_t *cont, rect_t rect, int propagate) +{ + DDIRTY(stderr, "->container_add_dirty_rel(%d,%d,%d,%d, %d)\n", GFX_PRINT_RECT(rect), propagate); + return cont->add_dirty_abs(cont, _move_rect(rect, gfx_point(cont->zone.x, cont->zone.y)), propagate); +} + +static inline void +_gfxw_set_container_ops(gfxw_container_t *container, gfxw_point_op *draw, gfxw_op *free, gfxw_op *tag, + gfxw_op_int *print, gfxw_bin_op *compare_to, gfxw_bin_op *equals, + gfxw_bin_op *superarea_of, gfxw_visual_op *set_visual, + gfxw_unary_container_op *free_tagged, gfxw_unary_container_op *free_contents, + gfxw_rect_op *add_dirty, gfxw_container_op *add) +{ + _gfxw_set_ops(GFXW(container), + draw, + free, + tag, + print, + compare_to, + equals, + superarea_of); + + container->free_tagged = free_tagged; + container->free_contents = free_contents; + container->add_dirty_abs = add_dirty; + container->add_dirty_rel = _gfxwop_container_add_dirty_rel; + container->add = add; + container->set_visual = set_visual; +} + +static int +_w_gfxwop_container_print_contents(const char *name, gfxw_widget_t *widget, int indentation) +{ + gfxw_widget_t *seeker = widget; + + indent(indentation); + + sciprintf("--%s:\n", name); + + while (seeker) { + seeker->print(seeker, indentation + 1); + sciprintf("\n"); + seeker = seeker->next; + } + + return 0; +} + +static int +_w_gfxwop_container_print(gfxw_widget_t *widget, int indentation) +{ + gfx_dirty_rect_t *dirty; + gfxw_container_t *container = (gfxw_container_t *) widget; + if (!GFXW_IS_CONTAINER(widget)) { + GFXERROR("_w_gfxwop_container_print() called on type %d widget\n", widget->type); + return 1; + } + + sciprintf(" viszone=((%d,%d),(%dx%d))\n", container->zone.x, container->zone.y, + container->zone.xl, container->zone.yl); + + indent(indentation); + sciprintf("--dirty:\n"); + + dirty = container->dirty; + while (dirty) { + indent(indentation + 1); + sciprintf("dirty(%d,%d, (%dx%d))\n", + dirty->rect.x, dirty->rect.y, dirty->rect.xl, dirty->rect.yl); + dirty = dirty->next; + } + + _w_gfxwop_container_print_contents("contents", container->contents, indentation); + + return 0; +} + + + +gfxw_container_t * +_gfxw_new_container_widget(rect_t area, int size, gfxw_widget_type_t type) +{ + gfxw_container_t *widget = (gfxw_container_t *) + _gfxw_new_widget(size, type); + + widget->bounds = widget->zone = area; + widget->contents = NULL; + widget->nextpp = &(widget->contents); + widget->dirty = NULL; + + widget->flags |= GFXW_FLAG_VISIBLE | GFXW_FLAG_CONTAINER; + + return widget; +} + + +static void +recursively_free_dirty_rects(gfx_dirty_rect_t *dirty) +{ + if (dirty) { + recursively_free_dirty_rects(dirty->next); + free(dirty); + } +} + + +int ti = 0; + +static inline int +_gfxw_dirty_rect_overlaps_normal_rect(rect_t port_zone, rect_t bounds, rect_t dirty) +{ + bounds.x += port_zone.x; + bounds.y += port_zone.y; + return gfx_rects_overlap(bounds, dirty); +} + +static int +_gfxwop_container_draw_contents(gfxw_widget_t *widget, gfxw_widget_t *contents) +{ + gfxw_container_t *container = (gfxw_container_t *) widget; + gfx_dirty_rect_t *dirty = container->dirty; + gfx_state_t *gfx_state = (widget->visual)? widget->visual->gfx_state : ((gfxw_visual_t *) widget)->gfx_state; + int draw_ports; + rect_t nullzone = {0,0,0,0}; + + if (!contents) + return 0; + + while (dirty) { + gfxw_widget_t *seeker = contents; + + while (seeker) { + if (_gfxw_dirty_rect_overlaps_normal_rect(GFXW_IS_CONTAINER(seeker)? nullzone : container->zone, + /* Containers have absolute coordinates, reflect this. */ + seeker->bounds, dirty->rect)) { + + if (GFXW_IS_CONTAINER(seeker)) {/* Propagate dirty rectangles /upwards/ */ + DDIRTY(stderr,"container_draw_contents: propagate upwards (%d,%d,%d,%d ,0)\n", GFX_PRINT_RECT(dirty->rect)); + ((gfxw_container_t *)seeker)->add_dirty_abs((gfxw_container_t *)seeker, dirty->rect, 0); + } + + seeker->flags |= GFXW_FLAG_DIRTY; + } + + seeker = seeker->next; + } + + dirty = dirty->next; + } + + /* The draw loop is executed twice: Once for normal data, and once for ports. */ + for (draw_ports = 0; draw_ports < 2; draw_ports++) { + + dirty = container->dirty; + + while (dirty) { + + gfxw_widget_t *seeker = contents; + while (seeker && (draw_ports || !GFXW_IS_PORT(seeker))) { + rect_t small_rect; + byte draw_noncontainers; + + memcpy(&small_rect, &(dirty->rect), sizeof(rect_t)); + draw_noncontainers = !_gfxop_clip(&small_rect, container->bounds); + + if (seeker->flags & GFXW_FLAG_DIRTY) { + + if (!GFXW_IS_CONTAINER(seeker) && draw_noncontainers) { + GFX_ASSERT(gfxop_set_clip_zone(gfx_state, small_rect)); + } + /* Clip zone must be reset after each element, because we might + ** descend into containers. + ** Doing this is relatively cheap, though. */ + if (draw_noncontainers || GFXW_IS_CONTAINER(seeker)) + seeker->draw(seeker, gfx_point(container->zone.x, container->zone.y)); + + if (!dirty->next) + seeker->flags &= ~GFXW_FLAG_DIRTY; + } + + seeker = seeker->next; + } + dirty = dirty->next; + } + } + /* Remember that the dirty rects should be freed afterwards! */ + + return 0; +} + +static int +_gfxwop_container_free(gfxw_widget_t *widget) +{ + gfxw_container_t *container = (gfxw_container_t *) widget; + gfxw_widget_t *seeker = container->contents; + + while (seeker) { + gfxw_widget_t *next = seeker->next; + seeker->widfree(seeker); + seeker = next; + } + + recursively_free_dirty_rects(container->dirty); + container->dirty = NULL; + + return _gfxwop_basic_free(widget); +} + +static int +_gfxwop_container_tag(gfxw_widget_t *widget) +{ + gfxw_container_t *container = (gfxw_container_t *) widget; + gfxw_widget_t *seeker = container->contents; + + while (seeker) { + seeker->tag(seeker); + seeker = seeker->next; + } + return 0; +} + + +static int +_w_gfxwop_container_set_visual_contents(gfxw_widget_t *contents, gfxw_visual_t *visual) +{ + while (contents) { + contents->set_visual(contents, visual); + contents = contents->next; + } + return 0; +} + +static int +_gfxwop_container_set_visual(gfxw_widget_t *widget, gfxw_visual_t *visual) +{ + gfxw_container_t *container = (gfxw_container_t *) widget; + + container->visual = visual; + if (widget->parent) { + if (!(GFXW_IS_LIST(widget) && !GFXWC(widget)->contents)) { + DDIRTY(stderr,"set_visual::DOWNWARDS abs(%d,%d,%d,%d, 1)\n", GFX_PRINT_RECT(widget->bounds)); + widget->parent->add_dirty_abs(widget->parent, widget->bounds, 1); + } + } + + return _w_gfxwop_container_set_visual_contents(container->contents, visual); +} + +static int +_gfxwop_container_free_tagged(gfxw_container_t *container) +{ + gfxw_widget_t *seekerp = (container->contents); + + while (seekerp) { + gfxw_widget_t *redshirt = seekerp; + + if (redshirt->flags & GFXW_FLAG_TAGGED) { + seekerp = (redshirt->next); + redshirt->widfree(redshirt); /* He's dead, Jim. */ + } else + seekerp = (seekerp)->next; + } + return 0; +} + +static int +_gfxwop_container_free_contents(gfxw_container_t *container) +{ + gfxw_widget_t *seeker = container->contents; + + while (seeker) { + gfxw_widget_t *next = seeker->next; + seeker->widfree(seeker); + seeker = next; + } + return 0; +} + +static void +_gfxw_dirtify_container(gfxw_container_t *container, gfxw_widget_t *widget) +{ + if (GFXW_IS_CONTAINER(widget)) + container->add_dirty_abs(GFXWC(container), widget->bounds, 1); + else + container->add_dirty_rel(GFXWC(container), widget->bounds, 1); +} + +static int +_parentize_widget(gfxw_container_t *container, gfxw_widget_t *widget) +{ + if (widget->parent) { + GFXERROR("_gfxwop_container_add(): Attempt to give second parent node to widget!\nWidget:"); + widget->print(GFXW(widget), 3); + sciprintf("\nContainer:"); + container->print(GFXW(container), 3); + + return 1; + } + + widget->parent = GFXWC(container); + + if (GFXW_IS_VISUAL(container)) + widget->set_visual(widget, (gfxw_visual_t *) container); + else if (container->visual) + widget->set_visual(widget, container->visual); + + return 0; +} + +static int +_gfxw_container_id_equals(gfxw_container_t *container, gfxw_widget_t *widget) +{ + gfxw_widget_t **seekerp = &(container->contents); + + if (GFXW_IS_PORT(widget)) + return 0; /* Don't match ports */ + + if (widget->ID == GFXW_NO_ID) + return 0; + + while (*seekerp + && ((*seekerp)->ID != widget->ID + || (*seekerp)->subID != widget->subID)) + seekerp = &((*seekerp)->next); + + if (!*seekerp) + return 0; + + if ((*seekerp)->equals(*seekerp, widget) + && !(*seekerp)->should_replace(*seekerp, widget)) { + widget->widfree(widget); + (*seekerp)->flags &= ~GFXW_FLAG_TAGGED; + return 1; + } else { + if (!(widget->flags & GFXW_FLAG_MULTI_ID)) + (*seekerp)->widfree(*seekerp); + return 0; + } +} + + +static int +_gfxwop_container_add_dirty(gfxw_container_t *container, rect_t dirty, int propagate) +{ +#if 0 + /* This code has been disabled because containers may contain sub-containers with + ** bounds greater than their own. */ + if (_gfxop_clip(&dirty, container->bounds)) + return 0; +#endif + + DDIRTY(stderr, "Effectively adding dirty %d,%d,%d,%d %d to ID %d\n", GFX_PRINT_RECT(dirty), propagate, container->ID); + container->dirty = gfxdr_add_dirty(container->dirty, dirty, GFXW_DIRTY_STRATEGY); + return 0; +} + + +static int +_gfxwop_container_add(gfxw_container_t *container, gfxw_widget_t *widget) +{ + if (_gfxw_container_id_equals(container, widget)) + return 0; + + if (_parentize_widget(container, widget)) + return 1; + + if (!(GFXW_IS_LIST(widget) && (!GFXWC(widget)->contents))) { /* Don't dirtify self on empty lists */ + DDIRTY(stderr, "container_add: dirtify DOWNWARDS (%d,%d,%d,%d, 1)\n", GFX_PRINT_RECT(widget->bounds)); + _gfxw_dirtify_container(container, widget); + } + + *(container->nextpp) = widget; + container->nextpp = &(widget->next); + + return 0; +} + +/*------------------------------*/ +/**** Lists and sorted lists ****/ +/*------------------------------*/ + +static int +_gfxwop_list_draw(gfxw_widget_t *list, point_t pos) +{ + DRAW_ASSERT(list, GFXW_LIST); + + _gfxwop_container_draw_contents(list, ((gfxw_list_t *)list)->contents); + recursively_free_dirty_rects(GFXWC(list)->dirty); + GFXWC(list)->dirty = NULL; + list->flags &= ~GFXW_FLAG_DIRTY; + return 0; +} + +static int +_gfxwop_sorted_list_draw(gfxw_widget_t *list, point_t pos) +{ + DRAW_ASSERT(list, GFXW_SORTED_LIST); + + _gfxwop_container_draw_contents(list, ((gfxw_list_t *)list)->contents); + recursively_free_dirty_rects(GFXWC(list)->dirty); + GFXWC(list)->dirty = NULL; + return 0; +} + +static inline int +_w_gfxwop_list_print(gfxw_widget_t *list, const char *name, int indentation) +{ + _gfxw_print_widget(list, indentation); + sciprintf(name); + return _w_gfxwop_container_print(list, indentation); +} + +static int +_gfxwop_list_print(gfxw_widget_t *list, int indentation) +{ + return _w_gfxwop_list_print(list, "LIST", indentation); +} + +static int +_gfxwop_sorted_list_print(gfxw_widget_t *list, int indentation) +{ + return _w_gfxwop_list_print(list, "SORTED_LIST", indentation); +} + +/* --- */ +#if 0 +struct gfxw_widget_list { + gfxw_widget_t *widget; + struct gfxw_widget_list *next; +}; + +static struct gfxw_widtet_list * +_gfxw_make_widget_list_recursive(gfxw_widget_t *widget) +{ + gfxw_widget_list *node; + + if (!widget) + return NULL; + + node = sci_malloc(sizeof(struct gfxw_widget_list)); + node->widget = widget; + node->next = _gfxw_make_widget_list_recursive(widget->next); + + return node; +} + +static struct gfxw_widget_list * +_gfxw_make_widget_list(gfxw_container_t *container) +{ + return _gfxw_make_widget_list_recursive(container->contents); +} +#endif +/* --- */ + + +static int +_gfxwop_list_equals(gfxw_widget_t *widget, gfxw_widget_t *other) + /* Requires identical order of list elements. */ +{ + gfxw_list_t *wlist, *olist; + + if (widget->type != other->type) + return 0; + + if (!GFXW_IS_LIST(widget)) { + GFXWARN("_gfxwop_list_equals(): Method called on non-list!\n"); + widget->print(widget, 0); + sciprintf("\n"); + return 0; + } + + wlist = (gfxw_list_t *) widget; + olist = (gfxw_list_t *) other; + + if (memcmp(&(wlist->bounds), &(olist->bounds), sizeof(rect_t))) + return 0; + + widget = wlist->contents; + other = olist->contents; + + while (widget && other) { + + if (!(widget->equals(widget, other) && !widget->should_replace(widget,other))) + return 0; + + widget = widget->next; + other = other->next; + } + + return (!widget && !other); /* True if both are finished now */ +} + +static int +_gfxwop_list_add_dirty(gfxw_container_t *container, rect_t dirty, int propagate) +{ + /* Lists add dirty boxes to both themselves and their parenting port/visual */ + + container->flags |= GFXW_FLAG_DIRTY; + + DDIRTY(stderr,"list_add_dirty %d,%d,%d,%d %d\n", GFX_PRINT_RECT(dirty), propagate); + if (propagate) + if (container->parent) { + DDIRTY(stderr, "->PROPAGATING\n"); + container->parent->add_dirty_abs(container->parent, dirty, 1); + } + + return _gfxwop_container_add_dirty(container, dirty, propagate); +} + +/* static inline */ int +_gfxwop_ordered_add(gfxw_container_t *container, gfxw_widget_t *widget, int compare_all) + /* O(n) */ +{ + gfxw_widget_t **seekerp = &(container->contents); + + if (widget->next) { + GFXERROR("_gfxwop_sorted_list_add(): Attempt to add widget to two lists!\nWidget:"); + widget->print(GFXW(widget), 3); + sciprintf("\nList:"); + container->print(GFXW(container), 3); + BREAKPOINT(); + + return 1; + } + + if (_gfxw_container_id_equals(container, widget)) + return 0; + + while (*seekerp && (compare_all || (widget->compare_to(widget, *seekerp) >= 0))) { + + if (widget->equals(GFXW(widget), GFXW(*seekerp))) { + if (compare_all) { + if ((*seekerp)->visual) + (*seekerp)->widfree(GFXW(*seekerp)); /* If it's a fresh widget */ + else + gfxw_annihilate(GFXW(*seekerp)); + + return _gfxwop_ordered_add(container, widget, compare_all); /* We might have destroyed the container's contents */ + } else { + widget->next = (*seekerp)->next; + (*seekerp)->widfree(GFXW(*seekerp)); + *seekerp = widget; + return (_parentize_widget(container, widget)); + } + } + + if (*seekerp) + seekerp = &((*seekerp)->next); + } + + widget->next = *seekerp; + *seekerp = widget; + + return _parentize_widget(container, widget); +} + +static int +_gfxwop_sorted_list_add(gfxw_container_t *container, gfxw_widget_t *widget) + /* O(n) */ +{ + return _gfxwop_ordered_add(container, widget, 0); +} + +void +_gfxw_set_ops_LIST(gfxw_container_t *list, char sorted) +{ + _gfxw_set_container_ops((gfxw_container_t *) list, + sorted? _gfxwop_sorted_list_draw : _gfxwop_list_draw, + _gfxwop_container_free, + _gfxwop_container_tag, + sorted? _gfxwop_sorted_list_print : _gfxwop_list_print, + _gfxwop_basic_compare_to, + sorted? _gfxwop_basic_equals : _gfxwop_list_equals, + _gfxwop_basic_superarea_of, + _gfxwop_container_set_visual, + _gfxwop_container_free_tagged, + _gfxwop_container_free_contents, + _gfxwop_list_add_dirty, + sorted? _gfxwop_sorted_list_add : _gfxwop_container_add); +} + +gfxw_list_t * +gfxw_new_list(rect_t area, int sorted) +{ + gfxw_list_t *list = (gfxw_list_t *) _gfxw_new_container_widget(area, sizeof(gfxw_list_t), + sorted? GFXW_SORTED_LIST : GFXW_LIST); + + _gfxw_set_ops_LIST(GFXWC(list), (char)sorted); + + return list; +} + + +/*---------------*/ +/**** Visuals ****/ +/*---------------*/ + +static int +_gfxwop_visual_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_visual_t *visual = (gfxw_visual_t *) widget; + gfx_dirty_rect_t *dirty = visual->dirty; + DRAW_ASSERT(widget, GFXW_VISUAL); + + while (dirty) { + int err = gfxop_clear_box(visual->gfx_state, dirty->rect); + + if (err) { + GFXERROR("Error while clearing dirty rect (%d,%d,(%dx%d))\n", dirty->rect.x, + dirty->rect.y, dirty->rect.xl, dirty->rect.yl); + if (err == GFX_FATAL) + return err; + } + + dirty = dirty->next; + } + + _gfxwop_container_draw_contents(widget, visual->contents); + + recursively_free_dirty_rects(visual->dirty); + visual->dirty = NULL; + widget->flags &= ~GFXW_FLAG_DIRTY; + + return 0; +} + +static int +_gfxwop_visual_free(gfxw_widget_t *widget) +{ + gfxw_visual_t *visual = (gfxw_visual_t *) widget; + gfxw_port_t **portrefs; + int retval; + + if (!GFXW_IS_VISUAL(visual)) { + GFXERROR("_gfxwop_visual_free() called on non-visual!Widget was: "); + widget->print(widget, 3); + return 1; + } + + portrefs = visual->port_refs; + + retval = _gfxwop_container_free(widget); + + free(portrefs); + return 0; +} + +static int +_gfxwop_visual_print(gfxw_widget_t *widget, int indentation) +{ + int i; + int comma = 0; + gfxw_visual_t *visual = (gfxw_visual_t *) widget; + + if (!GFXW_IS_VISUAL(visual)) { + GFXERROR("_gfxwop_visual_free() called on non-visual!Widget was: "); + widget->print(widget, 3); + return 1; + } + + _gfxw_print_widget(widget, indentation); + sciprintf("VISUAL; ports={"); + for (i = 0; i < visual->port_refs_nr; i++) + if (visual->port_refs[i]) { + if (comma) + sciprintf(","); + else + comma = 1; + + sciprintf("%d", i); + } + sciprintf("}\n"); + + return _w_gfxwop_container_print(widget, indentation); +} + +static int +_gfxwop_visual_set_visual(gfxw_widget_t *self, gfxw_visual_t *visual) +{ + if (self != GFXW(visual)) { + GFXWARN("Attempt to set a visual's parent visual to something else!\n"); + } else { + GFXWARN("Attempt to set a visual's parent visual!\n"); + } + return 1; +} + +void +_gfxw_set_ops_VISUAL(gfxw_container_t *visual) +{ + _gfxw_set_container_ops((gfxw_container_t *) visual, + _gfxwop_visual_draw, + _gfxwop_visual_free, + _gfxwop_container_tag, + _gfxwop_visual_print, + _gfxwop_basic_compare_to, + _gfxwop_basic_equals, + _gfxwop_basic_superarea_of, + _gfxwop_visual_set_visual, + _gfxwop_container_free_tagged, + _gfxwop_container_free_contents, + _gfxwop_container_add_dirty, + _gfxwop_container_add); +} + +gfxw_visual_t * +gfxw_new_visual(gfx_state_t *state, int font) +{ + gfxw_visual_t *visual = (gfxw_visual_t *) _gfxw_new_container_widget(gfx_rect(0, 0, 320, 200), sizeof(gfxw_visual_t), GFXW_VISUAL); + + visual->font_nr = font; + visual->gfx_state = state; + + visual->port_refs = (struct _gfxw_port**)sci_calloc(sizeof(gfxw_port_t), visual->port_refs_nr = 16); + + _gfxw_set_ops_VISUAL(GFXWC(visual)); + + return visual; +} + + +static int +_visual_find_free_ID(gfxw_visual_t *visual) +{ + int id = 0; + int newports = 16; + + while (visual->port_refs[id] && id < visual->port_refs_nr) + id++; + + if (id == visual->port_refs_nr) {/* Out of ports? */ + visual->port_refs = (struct _gfxw_port**)sci_realloc(visual->port_refs, visual->port_refs_nr += newports); + memset(visual->port_refs + id, 0, newports * sizeof(gfxw_port_t *)); /* Clear new port refs */ + } + + return id; +} + +static int +_gfxwop_add_dirty_rects(gfxw_container_t *dest, gfx_dirty_rect_t *src) +{ + DDIRTY(stderr, "Adding multiple dirty to #%d\n", dest->ID); + if (src) { + dest->dirty = gfxdr_add_dirty(dest->dirty, src->rect, GFXW_DIRTY_STRATEGY); + _gfxwop_add_dirty_rects(dest, src->next); + } + return 0; +} + +/*-------------*/ +/**** Ports ****/ +/*-------------*/ + +static int +_gfxwop_port_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_port_t *port = (gfxw_port_t *) widget; + DRAW_ASSERT(widget, GFXW_PORT); + + if (port->decorations) { + DDIRTY(stderr, "Getting/applying deco dirty (multi)\n"); + _gfxwop_add_dirty_rects(GFXWC(port->decorations), port->dirty); + if (port->decorations->draw(GFXW(port->decorations), gfxw_point_zero)) { + port->decorations->dirty = NULL; + return 1; + } + port->decorations->dirty = NULL; + } + + _gfxwop_container_draw_contents(widget, port->contents); + + recursively_free_dirty_rects(port->dirty); + port->dirty = NULL; + widget->flags &= ~GFXW_FLAG_DIRTY; + return 0; +} + +static int +_gfxwop_port_free(gfxw_widget_t *widget) +{ + gfxw_port_t *port = (gfxw_port_t *) widget; + + if (port->visual) { + gfxw_visual_t *visual = port->visual; + int ID = port->ID; + + if (ID < 0 || ID >= visual->port_refs_nr) { + GFXWARN("Attempt to free port #%d; allowed: [0..%d]!\n", ID, visual->port_refs_nr); + return GFX_ERROR; + } + + if (visual->port_refs[ID] != port) { + GFXWARN("While freeing port %d: Port is at %p, but port list indicates %p!\n", + ID, port, visual->port_refs[ID]); + } else visual->port_refs[ID] = NULL; + + } + + if (port->decorations) + port->decorations->widfree(GFXW(port->decorations)); + + return _gfxwop_container_free(widget); +} + +static int +_gfxwop_port_print(gfxw_widget_t *widget, int indentation) +{ + gfxw_port_t *port = (gfxw_port_t *) widget; + + _gfxw_print_widget(widget, indentation); + sciprintf("PORT"); + sciprintf(" font=%d drawpos=(%d,%d)", port->font_nr, port->draw_pos.x, port->draw_pos.y); + if (port->gray_text) + sciprintf(" (gray)"); + _w_gfxwop_container_print(GFXW(port), indentation); + return _w_gfxwop_container_print_contents("decorations", GFXW(port->decorations), indentation); + +} + +static int +_gfxwop_port_superarea_of(gfxw_widget_t *self, gfxw_widget_t *other) +{ + gfxw_port_t *port = (gfxw_port_t *) self; + + if (!port->port_bg) + return _gfxwop_basic_superarea_of(self, other); + + return port->port_bg->superarea_of(port->port_bg, other); +} + +static int +_gfxwop_port_set_visual(gfxw_widget_t *widget, gfxw_visual_t *visual) +{ + gfxw_list_t *decorations = ((gfxw_port_t *) widget)->decorations; + widget->visual = visual; + + if (decorations) + if (decorations->set_visual(GFXW(decorations), visual)) { + GFXWARN("Setting the visual for decorations failed for port "); + widget->print(widget, 1); + return 1; + } + + return _gfxwop_container_set_visual(widget, visual); +} + +static int +_gfxwop_port_add_dirty(gfxw_container_t *widget, rect_t dirty, int propagate) +{ + gfxw_port_t *self = (gfxw_port_t *) widget; + + self->flags |= GFXW_FLAG_DIRTY; + + _gfxwop_container_add_dirty(widget, dirty, propagate); + + DDIRTY(stderr,"Added dirty to ID %d\n", widget->ID); + DDIRTY(stderr, "dirty= (%d,%d,%d,%d) bounds (%d,%d,%d,%d)\n", dirty.x, dirty.x, dirty.xl, dirty.yl, + widget->bounds.x, widget->bounds.y, widget->bounds.xl, widget->bounds.yl); +#if 0 + /* FIXME: This is a worthwhile optimization */ + if (self->port_bg) { + gfxw_widget_t foo; + + foo.bounds = dirty; /* Yeah, sub-elegant, I know */ + foo.bounds.x -= self->zone.x; + foo.bounds.y -= self->zone.y; + if (self->port_bg->superarea_of(self->port_bg, &foo)) { + gfxw_container_t *parent = self->parent; + while (parent) { + fprintf(stderr,"Dirtifying parent id %d\n", parent->ID); + parent->flags |= GFXW_FLAG_DIRTY; + parent = parent->parent; + } + return 0; + } + } /* else propagate to the parent, since we're not 'catching' the dirty rect */ +#endif + + if (propagate) + if (self->parent) { + DDIRTY(stderr, "PROPAGATE\n"); + return self->parent->add_dirty_abs(self->parent, dirty, 1); + } + + return 0; +} + +static int +_gfxwop_port_add(gfxw_container_t *container, gfxw_widget_t *widget) + /* O(n) */ +{ + return _gfxwop_ordered_add(container, widget, 1); +} + +void +_gfxw_set_ops_PORT(gfxw_container_t *widget) +{ + _gfxw_set_container_ops((gfxw_container_t *) widget, + _gfxwop_port_draw, + _gfxwop_port_free, + _gfxwop_container_tag, + _gfxwop_port_print, + _gfxwop_basic_compare_to, + _gfxwop_basic_equals, + _gfxwop_port_superarea_of, + _gfxwop_port_set_visual, + _gfxwop_container_free_tagged, + _gfxwop_container_free_contents, + _gfxwop_port_add_dirty, + _gfxwop_port_add); +} + +gfxw_port_t * +gfxw_new_port(gfxw_visual_t *visual, gfxw_port_t *predecessor, rect_t area, gfx_color_t fgcolor, gfx_color_t bgcolor) +{ + gfxw_port_t *widget = (gfxw_port_t *) + _gfxw_new_container_widget(area, sizeof(gfxw_port_t), GFXW_PORT); + + VERIFY_WIDGET(visual); + + widget->port_bg = NULL; + widget->parent = NULL; + widget->decorations = NULL; + widget->title_text = NULL; + widget->draw_pos = gfx_point(0, 0); + widget->gray_text = 0; + widget->color = fgcolor; + widget->bgcolor = bgcolor; + widget->font_nr = visual->font_nr; + widget->ID = _visual_find_free_ID(visual); + widget->chrono_port = 0; + visual->port_refs[widget->ID] = widget; + + _gfxw_set_ops_PORT(GFXWC(widget)); + + return widget; +} + +void gfxw_port_auto_restore_background(gfxw_visual_t *visual, gfxw_port_t *window, rect_t auto_rect) +{ + window->port_flags |= WINDOW_FLAG_AUTO_RESTORE; + window->restore_snap = gfxw_make_snapshot(visual, auto_rect); +} + +gfxw_port_t * +gfxw_remove_port(gfxw_visual_t *visual, gfxw_port_t *port) +{ + gfxw_port_t *parent; + VERIFY_WIDGET(visual); + VERIFY_WIDGET(port); + + if (!visual->contents) { + GFXWARN("Attempt to remove port from empty visual\n"); + return NULL; + } + + parent = (gfxw_port_t *) port->parent; + if (port->port_flags & WINDOW_FLAG_AUTO_RESTORE) + gfxw_restore_snapshot(visual, port->restore_snap); + + if (port->widfree(GFXW(port))) + return parent; + + while (parent && !GFXW_IS_PORT(parent)) + parent = (gfxw_port_t *) parent->parent; /* Ascend through ancestors */ + + return parent; +} + + +gfxw_port_t * +gfxw_find_port(gfxw_visual_t *visual, int ID) +{ + if (ID < 0 || ID >= visual->port_refs_nr) + return NULL; + + return visual->port_refs[ID]; +} + + +gfxw_port_t * +gfxw_find_default_port(gfxw_visual_t *visual) +{ + int id = visual->port_refs_nr; + + while (id--) { + gfxw_port_t *port = visual->port_refs[id]; + + if (port) + return port; + } + + return NULL; +} + + +/*** - other functions - ***/ + +gfxw_widget_t * +gfxw_set_id(gfxw_widget_t *widget, int ID, int subID) +{ + if (widget) { + widget->ID = ID; + widget->subID = subID; + } + + return widget; +} + +gfxw_dyn_view_t * +gfxw_dyn_view_set_params(gfxw_dyn_view_t *widget, int under_bits, void *under_bitsp, + int signal, void *signalp) +{ + if (!widget) + return NULL; + + widget->under_bits = under_bits; + widget->under_bitsp = under_bitsp; + widget->signal = signal; + widget->signalp = signalp; + + return widget; +} + +gfxw_widget_t * +gfxw_remove_id(gfxw_container_t *container, int ID, int subID) +{ + gfxw_widget_t **wp = &(container->contents); + + while (*wp) { + if ((*wp)->ID == ID + && (subID == GFXW_NO_ID + || (*wp)->subID == subID)) { + gfxw_widget_t *widget = *wp; + + *wp = (*wp)->next; + widget->next = NULL; + widget->parent = NULL; + widget->visual = NULL; + + return widget; + } + + wp = &((*wp)->next); + } + + return NULL; +} + + +gfxw_widget_t * +gfxw_hide_widget(gfxw_widget_t *widget) +{ + if (widget->flags & GFXW_FLAG_VISIBLE) { + widget->flags &= ~GFXW_FLAG_VISIBLE; + + if (widget->parent) + widget->parent->add_dirty_rel(widget->parent, widget->bounds, 1); + } + + return widget; +} + +gfxw_widget_t * +gfxw_show_widget(gfxw_widget_t *widget) +{ + if (!(widget->flags & GFXW_FLAG_VISIBLE)) { + widget->flags |= GFXW_FLAG_VISIBLE; + + if (widget->parent) + widget->parent->add_dirty_rel(widget->parent, widget->bounds, 1); + } + + return widget; +} + + +gfxw_snapshot_t * +gfxw_make_snapshot(gfxw_visual_t *visual, rect_t area) +{ + gfxw_snapshot_t *retval = (gfxw_snapshot_t*)sci_malloc(sizeof(gfxw_snapshot_t)); + + retval->serial = widget_serial_number_counter++; + + retval->area = area; + + /* Work around subset semantics in gfx_rect_subset. + This fixes the help icon in LSL5. */ + if (retval->area.xl == 320) retval->area.xl = 321; + + return retval; +} + + +int +gfxw_widget_matches_snapshot(gfxw_snapshot_t *snapshot, gfxw_widget_t *widget) +{ + int free_below = (snapshot->serial < widget_serial_number_counter)? 0: widget_serial_number_counter; + int free_above_eq = snapshot->serial; + rect_t bounds = widget->bounds; + + if (!GFXW_IS_CONTAINER(widget) && widget->parent) { + bounds.x += widget->parent->bounds.x; + bounds.y += widget->parent->bounds.y; + } + + return ((widget->serial >= free_above_eq + || widget->serial < free_below) + && gfx_rect_subset(bounds, snapshot->area)); +} + +#define MAGIC_FREE_NUMBER -42 + +void +_gfxw_free_contents_appropriately(gfxw_container_t *container, gfxw_snapshot_t *snapshot, int priority) +{ + gfxw_widget_t *widget = container->contents; + + while (widget) { + gfxw_widget_t *next = widget->next; + + if (gfxw_widget_matches_snapshot(snapshot, widget) && !(widget->flags & GFXW_FLAG_IMMUNE_TO_SNAPSHOTS) + && (priority == MAGIC_FREE_NUMBER || priority <= widget->widget_priority || widget->widget_priority == -1)) { + widget->widfree(widget); + } else { + if (GFXW_IS_CONTAINER(widget)) + _gfxw_free_contents_appropriately(GFXWC(widget), snapshot, priority); + } + + widget = next; + } +} + +gfxw_snapshot_t * +gfxw_restore_snapshot(gfxw_visual_t *visual, gfxw_snapshot_t *snapshot) +{ + _gfxw_free_contents_appropriately(GFXWC(visual), snapshot, MAGIC_FREE_NUMBER); + + return snapshot; +} + +void +gfxw_annihilate(gfxw_widget_t *widget) +{ + gfxw_visual_t *visual = widget->visual; + int widget_priority = 0; + int free_overdrawn = 0; + + gfxw_snapshot_t snapshot; + if (!GFXW_IS_CONTAINER(widget) && widget->parent && visual && (widget->flags & GFXW_FLAG_VISIBLE)) { + snapshot.serial = 0; + snapshot.area = widget->bounds; + snapshot.area.x += widget->parent->zone.x; + snapshot.area.y += widget->parent->zone.y; + free_overdrawn = 1; + widget_priority = widget->widget_priority; + } + + widget->widfree(GFXW(widget)); + + if (free_overdrawn) + _gfxw_free_contents_appropriately(GFXWC(visual), &snapshot, widget_priority); +} + + + +gfxw_dyn_view_t * +gfxw_picviewize_dynview(gfxw_dyn_view_t *dynview) +{ + dynview->type = GFXW_PIC_VIEW; + dynview->flags |= GFXW_FLAG_DIRTY; + + _gfxw_set_ops_PICVIEW(GFXW(dynview)); + + if (dynview->parent) + _gfxw_dirtify_container(dynview->parent, GFXW(dynview)); + + return dynview; +} + +/* Chrono-Ports (tm) */ + +gfxw_port_t * +gfxw_get_chrono_port(gfxw_visual_t *visual, gfxw_list_t **temp_widgets_list, int flags) +{ + gfxw_port_t *result = NULL; + gfx_color_t transparent = {0}; + int id = 0; + + if (!(flags & GFXW_CHRONO_NON_TOPMOST)) + { + result = gfxw_find_default_port(visual); + } else + { + id = visual->port_refs_nr; + while (id >= 0 && (!visual->port_refs[id] || + !visual->port_refs[id]->chrono_port)) + id--; + + if (id >= 0) + result = visual->port_refs[id]; + } + + if (!result || !result->chrono_port) + { + if (flags & GFXW_CHRONO_NO_CREATE) return NULL; + result = gfxw_new_port(visual, NULL, gfx_rect(0, 0, 320, 200), transparent, transparent); + *temp_widgets_list = gfxw_new_list(gfx_rect(0, 0, 320, 200), 1); + result->add(GFXWC(result), GFXW(*temp_widgets_list)); + result->chrono_port = 1; + if (temp_widgets_list) + *temp_widgets_list = GFXWC(result->contents); + return result; + }; + + if (temp_widgets_list) + *temp_widgets_list = GFXWC(result->contents); + return result; +} + +static int +gfxw_check_chrono_overlaps(gfxw_port_t *chrono, gfxw_widget_t *widget) +{ + gfxw_widget_t *seeker = GFXWC(chrono->contents)->contents; + + while (seeker) { + if (gfx_rect_equals(seeker->bounds, widget->bounds)) + { + gfxw_annihilate(GFXW(seeker)); + return 1; + } + + seeker = seeker->next; + } + + return 0; +} + +void +gfxw_add_to_chrono(gfxw_visual_t *visual, gfxw_widget_t *widget) +{ + gfxw_list_t *tw; + gfxw_port_t *chrono = + gfxw_get_chrono_port(visual, &tw, 0); + + gfxw_check_chrono_overlaps(chrono, widget); + chrono->add(GFXWC(chrono), widget); +} + +static gfxw_widget_t * +gfxw_widget_intersects_chrono(gfxw_list_t *tw, gfxw_widget_t *widget) +{ + gfxw_widget_t *seeker; + + assert(tw->type == GFXW_SORTED_LIST); + + seeker = tw->contents; + while (seeker) + { + point_t origin; + rect_t bounds = widget->bounds; + + bounds = widget->bounds; + origin.x = seeker->parent->zone.x; + origin.y = seeker->parent->zone.y; + gfx_rect_translate(bounds, origin); + + if (gfx_rects_overlap(bounds, seeker->bounds)) + return seeker; + + seeker = seeker->next; + } + + return 0; +} + +void +gfxw_widget_reparent_chrono(gfxw_visual_t *visual, gfxw_widget_t *view, gfxw_list_t *target) +{ + gfxw_list_t *tw; + gfxw_port_t *chrono; + gfxw_widget_t *intersector; + + chrono = gfxw_get_chrono_port(visual, &tw, GFXW_CHRONO_NO_CREATE); + if (chrono == NULL) return; + + intersector = gfxw_widget_intersects_chrono(tw, view); + if (intersector) + { + point_t origin = gfx_point(intersector->parent->zone.x, + intersector->parent->zone.y); + + gfxw_remove_widget_from_container(GFXWC(chrono), GFXW(tw)); + gfxw_remove_widget_from_container(GFXWC(chrono->parent), GFXW(chrono)); + gfxw_annihilate(GFXW(chrono)); + + gfx_rect_translate(tw->zone, origin); + target->add(GFXWC(target), GFXW(tw)); + } +} + +void +gfxw_widget_kill_chrono(gfxw_visual_t *visual, int window) +{ + int i; + + for (i=window; i < visual->port_refs_nr ; i++) + { + if (visual->port_refs[i] && visual->port_refs[i]->chrono_port) + gfxw_annihilate(GFXW(visual->port_refs[i])); + } +} diff --git a/engines/sci/gfx/wrapper.c b/engines/sci/gfx/wrapper.c new file mode 100644 index 0000000000..0bf1097c2a --- /dev/null +++ b/engines/sci/gfx/wrapper.c @@ -0,0 +1,26 @@ +/*************************************************************************** + wrapper.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ diff --git a/engines/sci/include/Makefile.am b/engines/sci/include/Makefile.am new file mode 100644 index 0000000000..316690244c --- /dev/null +++ b/engines/sci/include/Makefile.am @@ -0,0 +1,19 @@ +SUBDIRS = win32 beos +EXTRA_DIST = old_objects.h kernel.h event.h \ + kdebug.h sci_conf.h resource.h script.h \ + vocabulary.h uinput.h console.h script.h vm.h \ + engine.h util.h menubar.h versions.h sci_dos.h \ + gfx_driver.h gfx_operations.h gfx_options.h \ + gfx_resmgr.h gfx_resource.h gfx_widgets.h \ + gfx_state_internal.h gfx_system.h gfx_tools.h \ + sci_widgets.h scitypes.h sbtree.h \ + sciresource.h modules.h sci_memory.h \ + hashmap.h int_hashmap.h sys_strings.h heapmgr.h \ + sfx_iterator.h sfx_songlib.h \ + sfx_engine.h vm_types.h seg_manager.h \ + sfx_timer.h sfx_player.h gfx_res_options.h \ + sfx_time.h sci_midi.h \ + sfx_core.h sfx_pcm.h listener.h \ + sfx_iterator_internal.h game_select.h \ + reg_t_hashmap.h list.h aatree.h + diff --git a/engines/sci/include/aatree.h b/engines/sci/include/aatree.h new file mode 100644 index 0000000000..1f16d2391b --- /dev/null +++ b/engines/sci/include/aatree.h @@ -0,0 +1,85 @@ +/*************************************************************************** + aatree.h Copyright (C) 2006 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik [w.f.b.w.v.niftrik@stud.tue.nl] + +***************************************************************************/ + +#ifndef _SCI_AATREE_H +#define _SCI_AATREE_H + +/* Andersson tree implementation. Stores data pointers in a balanced binary +** tree. A user-supplied comparison function defines the ordering. For the +** semantics of this function see qsort(3) +*/ + +typedef struct aatree aatree_t; + +/* Left child */ +#define AATREE_WALK_LEFT 0 +/* Right child */ +#define AATREE_WALK_RIGHT 1 + +aatree_t *aatree_new(); +/* Allocates a new aatree +** Parameters: (void) +** Returns : (aatree_t *) A newly allocated aatree +*/ + +void aatree_free(aatree_t *t); +/* Deallocates an aatree +** Parameters: (aatree_t *) t: The aatree +** Returns : (void) +*/ + +int aatree_delete(void *x, aatree_t **t, int (*compar)(const void *, const void *)); +/* Deletes a data element from an aatree +** Parameters: (void *) x: The data element to delete +** (aatree_t **) t: The aatree +** compar: The comparison function +** Returns : (int) 0 on success, -1 if x wasn't found in t +*/ + +int aatree_insert(void *x, aatree_t **t, int (*compar)(const void *, const void *)); +/* Inserts a data element into an aatree +** Parameters: (void *) x: The data element to insert +** (aatree_t **) t: The aatree +** compar: The comparison function +** Returns : (int) 0 on success, -1 if x already exists in t +*/ + +aatree_t *aatree_walk(aatree_t *t, int direction); +/* Walks to either the left or right child of a node +** Parameters: (aatree_t *) t: The node +** (int) direction: AATREE_WALK_LEFT or AATREE_WALK_RIGHT +** Returns : (aatree_t *) The requested child of t or NULL if it doesn't +** exist +*/ + +void *aatree_get_data(aatree_t *t); +/* Returns the data element of a node +** Parameters: (aatree_t *) t: The node +** Returns : (void *) The data element +*/ + +#endif /* !_SCI_AATREE_H */ diff --git a/engines/sci/include/beos/Makefile.am b/engines/sci/include/beos/Makefile.am new file mode 100644 index 0000000000..d449e08e0a --- /dev/null +++ b/engines/sci/include/beos/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = collsyms.h fnmatch.h \ No newline at end of file diff --git a/engines/sci/include/beos/collsyms.h b/engines/sci/include/beos/collsyms.h new file mode 100644 index 0000000000..7ef0906879 --- /dev/null +++ b/engines/sci/include/beos/collsyms.h @@ -0,0 +1,129 @@ +/* collsyms.h -- collating symbol names and their corresponding characters + (in ascii) as given by POSIX.2 in table 2.8. */ + +/* Copyright (C) 1997 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2, or (at your option) any later + version. + + Bash is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with Bash; see the file COPYING. If not, write to the Free Software + Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ + +#ifndef _COLLSYMS_H_ +# define _COLLSYSMS_H_ + +/* The upper-case letters, lower-case letters, and digits are omitted from + this table. The digits are not included in the table in the POSIX.2 + spec. The upper and lower case letters are translated by the code + in fnmatch.c:collsym(). */ + +typedef struct _collsym { + char *name; + char code; +} COLLSYM; + +static COLLSYM posix_collsyms[] = +{ + "NUL", '\0', + "SOH", '\001', + "STX", '\002', + "ETX", '\003', + "EOT", '\004', + "ENQ", '\005', + "ACK", '\006', +#ifdef __STDC__ + "alert", '\a', +#else + "alert", '\007', +#endif + "backspace", '\b', + "tab", '\t', + "newline", '\n', + "vertical-tab", '\v', + "form-feed", '\f', + "carriage-return", '\r', + "SO", '\016', + "SI", '\017', + "DLE", '\020', + "DC1", '\021', + "DC2", '\022', + "DC3", '\023', + "DC4", '\024', + "NAK", '\025', + "SYN", '\026', + "ETB", '\027', + "CAN", '\030', + "EM", '\031', + "SUB", '\032', + "ESC", '\033', + "IS4", '\034', + "IS3", '\035', + "IS2", '\036', + "IS1", '\037', + "space", ' ', + "exclamation-mark", '!', + "quotation-mark", '"', + "number-sign", '#', + "dollar-sign", '$', + "percent-sign", '%', + "ampersand", '&', + "apostrophe", '\'', + "left-parenthesis", '(', + "right-parenthesis", ')', + "asterisk", '*', + "plus-sign", '+', + "comma", ',', + "hyphen", '-', + "minus", '-', /* extension from POSIX.2 */ + "dash", '-', /* extension from POSIX.2 */ + "period", '.', + "slash", '/', + "solidus", '/', /* extension from POSIX.2 */ + "zero", '0', + "one", '1', + "two", '2', + "three", '3', + "four", '4', + "five", '5', + "six", '6', + "seven", '7', + "eight", '8', + "nine", '9', + "colon", ':', + "semicolon", ';', + "less-than-sign", '<', + "equals-sign", '=', + "greater-than-sign", '>', + "question-mark", '?', + "commercial-at", '@', + /* upper-case letters omitted */ + "left-square-bracket",'[', + "backslash", '\\', + "reverse-solidus", '\\', + "right-square-bracket", ']', + "circumflex", '^', + "circumflex-accent", '^', /* extension from POSIX.2 */ + "underscore", '_', + "grave-accent", '`', + /* lower-case letters omitted */ + "left-brace", '{', /* extension from POSIX.2 */ + "left-curly-bracket", '{', + "vertical-line", '|', + "right-brace", '}', /* extension from POSIX.2 */ + "right-curly-bracket", '}', + "tilde", '~', + "DEL", '\177', + 0, 0, +}; + +#endif diff --git a/engines/sci/include/beos/fnmatch.h b/engines/sci/include/beos/fnmatch.h new file mode 100644 index 0000000000..7aa1164236 --- /dev/null +++ b/engines/sci/include/beos/fnmatch.h @@ -0,0 +1,49 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111 USA. */ + +#ifndef _FNMATCH_H +#define _FNMATCH_H 1 + +/* #include "stdc.h" */ + +/* We #undef these before defining them because some losing systems + (HP-UX A.08.07 for example) define these in . */ +#undef FNM_PATHNAME +#undef FNM_NOESCAPE +#undef FNM_PERIOD + +/* Bits set in the FLAGS argument to `fnmatch'. */ +/* standard flags */ +#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */ +#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */ +#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */ + +/* extended flags */ +#define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */ +#define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */ +#define FNM_EXTMATCH (1 << 5) /* Use ksh-like extended matching. */ + +/* Value returned by `fnmatch' if STRING does not match PATTERN. */ +#define FNM_NOMATCH 1 + +/* Match STRING against the filename pattern PATTERN, + returning zero if it matches, FNM_NOMATCH if not. */ +/* extern int fnmatch __P((char *, char *, int)); */ +extern int fnmatch (char *, char *, int); + +#endif /* _FNMATCH_H */ diff --git a/engines/sci/include/conf_driver.h b/engines/sci/include/conf_driver.h new file mode 100644 index 0000000000..a415adf396 --- /dev/null +++ b/engines/sci/include/conf_driver.h @@ -0,0 +1,204 @@ +/*************************************************************************** + conf_driver.h Copyright (C) 2007 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef CONF_DRIVER_H_ +#define CONF_DRIVER_H_ + + +/* Possible values for enum types */ +typedef struct { + char *name; + int value; +} conf_value_t; + +typedef struct { + char *name; + char *description; + unsigned int type; + unsigned int flags; + union { char * str; + int nr } default; /* Optional default value */ + int min; /* For subrange types */ + int max; /* For subrange types; for enum types */ + conf_value_t *values; /* For enum types; NULL-terminated */ + void *data; /* User data */ +} conf_option_t; + +#define CONF_OPTION_TYPE_INT 0 +#define CONF_OPTION_TYPE_SUBRANGE 1 /* ``interval'' type, i.e. bounded int */ +#define CONF_OPTION_TYPE_STRING 2 +#define CONF_OPTION_TYPE_BOOL 3 /* special built-in enum */ +#define CONF_OPTION_TYPE_ENUM 4 + +#define CONF_OPTION_FLAG_MANDATORY (1<<0) /* Option MUST be set */ +#define CONF_OPTION_FLAG_HAS_DEFAULT (1<<1) /* Option has a default value */ +#define CONF_OPTION_FLAG_DYNAMIC (1<<2) /* Option may be altered after initialisation */ + + +#define CONF_DRIVER_FAIL 1 +#define CONF_DRIVER_SUCCESS 0 + +typedef struct conf_header { + char [16] magic; /* MUST be CONF_DRIVER_MAGIC */ + int freesci_version; /* MUST be CONF_DRIVER_VERSION */ + int subsystem_id; /* CONF_SUBSYSTEM_*, if appropriate */ + int subsystem_version; + char *name; /* Long driver name */ + char *version; /* Long driver version */ + unsigned int dependencies; /* CONF_DRIVER_DEPENDENCY_* */ + conf_option_t *options; /* Last option has name of NULL */ + char * (*set_option)(void * self, /* points to base struct */ + conf_option_t *, + union { char *str; + int nr }); /* Set option, return static error (if applicable) or NULL on success */ +} conf_header_t; /* Universal driver header */ + +struct conf_driver; +struct conf_subsystem; +struct conf_main; + +#define CONF_SUBSYSTEM_GFX 0 +#define CONF_SUBSYSTEM_SOUND 1 /* The sfx/player subsystem */ +#define CONF_SUBSYSTEM_PCM_DEVICE 2 +#define CONF_SUBSYSTEM_MIXER 3 +#define CONF_SUBSYSTEM_SEQ 4 /* HW sequencer or generic softseq interface */ +#define CONF_SUBSYSTEM_SOFTSEQ 5 +#define CONF_SUBSYSTEM_TIMER 6 + +#define CONF_SUBSYSTEMS_NR 7 + +typedef struct conf_driver { + conf_header_t header; + char * (*init)(struct conf_driver *self, + struct conf_subsystem *owner); /* Initialise, return static error message on error or NULL on success. + ** The owner is guaranteed to have been configured and guaranteed NOT to have + ** been initialised. */ + + void (*exit)(void); +} conf_driver_t; + +typedef struct conf_subsystem { + conf_header_t header; + char * (*init)(struct conf_subsystem *self, + struct conf_main *main, + struct conf_driver *driver); /* Initialise, return static error message on error or NULL on success. + ** The driver is configured and initialised, the main reference configured but + ** not initialised. */ + void (*exit)(void); + char *(*get_default_driver)(struct conf_subsystem *self, + int index); /* Get the nth default driver name, or NULL if there is none. These are tried in order if + ** there is no explicit choice. */ + conf_driver_t **builtin_drivers; /* NULL terminated list of built-in drivers */ + char *dynamic_driver_prefix; /* string prefix to dynamically loaded drivers for this subsystem */ +} conf_subsystem_t; + + +typedef struct conf_main { + conf_header_t header; + conf_subsystem_t *[CONF_SUBSYSTEMS_NR]; + char **dynamic_driver_locations; /* NULL terminated list of locations to check for dynamically loadable drivers */ + char * (*init)(struct conf_main *self); /* Return static error message, or NULL on success. */ + void (*exit)(void); +} conf_main_t; + + +#define CONF_DRIVER_MAGIC "FreeSCI driver" + +#define CONF_DRIVER_VERSION 1 + +/* Dependency flags */ +#define CONF_DRIVER_DEPENDENCY_GFX (1 << CONF_SUBSYSTEM_GFX) +#define CONF_DRIVER_DEPENDENCY_SOUND (1 << CONF_SUBSYSTEM_SOUND) +#define CONF_DRIVER_DEPENDENCY_PCM_DEVICE (1 << CONF_SUBSYSTEM_PCM_DEVICE) +#define CONF_DRIVER_DEPENDENCY_MIXER (1 << CONF_SUBSYSTEM_MIXER) +#define CONF_DRIVER_DEPENDENCY_SEQ (1 << CONF_SUBSYSTEM_SEQ) +#define CONF_DRIVER_DEPENDENCY_SOFTSEQ (1 << CONF_SUBSYSTEM_SOFTSEQ) +#define CONF_DRIVER_DEPENDENCY_TIMER (1 << CONF_SUBSYSTEM_TIMER) + +#define CONF_BUILTIN_DEPENDENCIES (CONF_DRIVER_DEPENDENCY_GFX | CONF_DRIVER_DEPENDENCY_SOUND) + +/* ---------------------------------------------- */ +/* -- convenience macros for driver developers -- */ +/* ---------------------------------------------- */ + + +char * +conf_default_set_option(void * self, conf_option_t *option, + union { char *str; + int nr } value); +/* Default implementation of the option setting function +** Parameters: (void *) self: Reference to the structure we should be accessing +** (conf_option_t *) option: The option to set +** (char * | nr) value: The value to set +** Returns : (char *) NULL on success, a static error string otherwise +*/ + + +#define CONF_MK_HEADER(NAME, VERSION, SUBSYSTEM, DEPENDENCIES, OPTIONS, SETOPTION) \ + { \ + CONF_DRIVER_MAGIC, \ + CONF_DRIVER_VERSION, \ + SUBSYSTEM, \ + 0, \ + NAME, \ + VERSION, \ + DEPENDENCIES, \ + OPTIONS, \ + SETOPTION } + +#define CONF_OPT_ANY(STRUCTURE, FIELD, NAME, TYPE, DEFAULT, MIN, MAX, VALUES, FLAGS, DESCRIPTION) \ + { \ + NAME, \ + DESCRIPTION, \ + TYPE, \ + FLAGS, \ + DEFAULT, \ + MIN, \ + MAX, \ + VALUES, \ + (void *)(offsetof (STRUCTURE, FIELD)) \ + } + +#define CONF_OPT_INT(S, F, NAME, DEFAULT, FLAGS, DESCR) CONF_OPT_ANY(S, F, NAME, CONF_OPTION_TYPE_INT, {.nr = DEFAULT}, 0, 0, NULL, (FLAGS) | CONF_OPTION_HAS_DEFAULT, DESCR) +#define CONF_OPT_INT_NODEFAULT(S, F, NAME, FLAGS, DESCR) CONF_OPT_ANY(S, F, NAME, CONF_OPTION_TYPE_INT, {.nr = 0}, 0, 0, NULL, (FLAGS), DESCR) + +#define CONF_OPT_SUBRANGE(S, F, NAME, DEFAULT, MIN, MAX, FLAGS, DESCR) CONF_OPT_ANY(S, F, NAME, CONF_OPTION_TYPE_SUBRANGE, {.nr = DEFAULT}, MIN, MAX, NULL, (FLAGS) | CONF_OPTION_HAS_DEFAULT, DESCR) +#define CONF_OPT_SUBRANGE_NODEFAULT(S, F, NAME, MIN, MAX, FLAGS, DESCR) CONF_OPT_ANY(S, F, NAME, CONF_OPTION_TYPE_SUBRANGE, {.nr = 0}, MIN, MAX, NULL, (FLAGS), DESCR) + +#define CONF_OPT_STRING(S, F, NAME, DEFAULT, FLAGS, DESCR) CONF_OPT_ANY(S, F, NAME, CONF_OPTION_TYPE_STRING, {.str = DEFAULT}, 0, 0, NULL, (FLAGS) | CONF_OPTION_HAS_DEFAULT, DESCR) +#define CONF_OPT_STRING_NODEFAULT(S, F, NAME, FLAGS, DESCR) CONF_OPT_ANY(S, F, NAME, CONF_OPTION_TYPE_STRING, {.str = NULL}, 0, 0, NULL, (FLAGS), DESCR) + +#define CONF_OPT_BOOL(S, F, NAME, DEFAULT, FLAGS, DESCR) CONF_OPT_ANY(S, F, NAME, CONF_OPTION_TYPE_INT, {.nr = DEFAULT}, 0, 0, NULL, (FLAGS) | CONF_OPTION_HAS_DEFAULT, DESCR) +#define CONF_OPT_BOOL_NODEFAULT(S, F, NAME, FLAGS, DESCR) CONF_OPT_ANY(S, F, NAME, CONF_OPTION_TYPE_INT, {.nr = 0}, 0, 0, NULL, (FLAGS), DESCR) + +#define CONF_OPT_ENUM(S, F, NAME, DEFAULT, CHOICES, FLAGS, DESCR) CONF_OPT_ANY(S, F, NAME, CONF_OPTION_TYPE_INT, {.nr = DEFAULT}, 0, 0, CHOICES, (FLAGS) | CONF_OPTION_HAS_DEFAULT, DESCR) +#define CONF_OPT_ENUM_NODEFAULT(S, F, NAME, CHOICES, FLAGS, DESCR) CONF_OPT_ANY(S, F, NAME, CONF_OPTION_TYPE_INT, {.nr = 0}, 0, 0, CHOICES, (FLAGS), DESCR) + +#define CONF_LAST_OPTION { NULL, NULL, -1, 0, 0, 0, 0, NULL } + + +#endif /* !defined(CONF_DRIVER_H_) */ diff --git a/engines/sci/include/conf_extension.h b/engines/sci/include/conf_extension.h new file mode 100644 index 0000000000..768e8f28c3 --- /dev/null +++ b/engines/sci/include/conf_extension.h @@ -0,0 +1,71 @@ +/*************************************************************************** + Copyright (C) 2008 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantability, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +/* Config extensions handle special-purpose configuration options such as the +** graphics operations. To add new special-purpose operations, modify this file, +** the lexer, and conf_extensions.c. +*/ + +#ifndef CONF_EXTENSION_H_ +#define CONF_EXTENSION_H_ + +#include +#include + +#define CONF_EXT_TYPE_INVALID -1 +#define CONF_EXT_TYPE_GFX 0 + +typedef struct { + int type; /* CONF_EXT_TYPE_* */ + void *data; +} conf_extension_t; + + + +int +conf_extension_supercedes(conf_extension_t *a, conf_extension_t *b); +/* Determines whether conf extension b shadows extension a +** Parameters: (conf_extension_t *) a: The ``earlier'' extension +** (conf_extension_t *) b: The ``later'' extension +** Returns : nonzero iff shadowing does occur +*/ + +void +conf_extension_print(FILE *file, conf_extension_t *a); +/* Prints out a config extension +** Parameters: (FILE *) file: The file to print to +** (conf_extension_t *) a: The extension to print out +*/ + +void +conf_extension_free(conf_extension_t *a); +/* Frees up all data associated with a conf extension +** Parameters: (conf_extension_t *) a: The extension to free +** This also frees up a itself. +*/ + +#endif /* !defined CONF_EXTENSION_H_ */ diff --git a/engines/sci/include/conf_parse.h b/engines/sci/include/conf_parse.h new file mode 100644 index 0000000000..d5ffd5b9da --- /dev/null +++ b/engines/sci/include/conf_parse.h @@ -0,0 +1,108 @@ +/*************************************************************************** + conf_parse.h Copyright (C) 2007 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +/* RAW parse (config layers zero and one) */ + +#ifndef CONF_PARSE_H_ +#define CONF_PARSE_H_ +#include + +#define CONF_PARSE_TYPE_WHITESPACE 1 /* Whitespace, comments */ +#define CONF_PARSE_TYPE_SUBSECTION 2 /* Subsection division */ +#define CONF_PARSE_TYPE_OPTION 3 /* Option or driver assignment */ +#define CONF_PARSE_TYPE_INCLUDE 4 /* External include file */ +#define CONF_PARSE_TYPE_EXTENSION 5 /* An ``extension'' */ +#define CONF_PARSE_TYPE_LEXERROR 255 /* Something lexically broken (stored in ``whitespace'') */ + +/* Base options */ +#define CONF_PARSE_BASEOPTION_VERSION 0 +#define CONF_PARSE_BASEOPTION_RESOURCEDIR 1 +#define CONF_PARSE_BASEOPTION_DEBUGMODE 2 + +typedef struct _conf_parse_struct { + int type; /* c. CONF_PARSE_TYPE */ + int line_nr; /* Line number in the current input file */ + + struct _conf_parse_struct *prev; + struct _conf_parse_struct *next; + struct _conf_parse_struct *overridden; /* In case we overrode something */ + + union { + char *whitespace; + struct { + char *subsystem; /* may be NULL if driver==NULL */ + char *driver; /* may be NULL */ + char *option; + char *centre_whitespace; + char *value; + char *terminal_whitespace; + } assignment; /* driver assignment, option */ + struct { + char *pre_whitespace; + char *name; + char *post_whitespace; + } subsection; + struct { + char *include_prefix; /* NULL for system includes */ + char *filename; /* Extracted from include_stmt, also a copy */ + char *include_suffix; /* NULL for system includes */ + int modifiable; /* May we write to this file? */ + struct _conf_parse_struct *options_head; + struct _conf_parse_struct *options_end; + } include; + conf_extension_t *extension; /* Extended information, cf. conf_extension.h */ + } data; +} conf_parse_t; + + + +conf_parse_t * +conf_read_file(char *config_file_name, int modifiable, conf_parse_t *previous); +/* Read configuration file +** Parameters: (char *) config_file_name: File name for the source config file +** (int) modifiable: Whether to treat the result as modifiable +** (conf_parse_t *) previous: Head of the previous configuration, or NULL if none +** Returns : (conf_parse_t *) Head of the combined config parse, or NULL on failure +** Effects : (stderr) Error message if the specified file was found but parsing failed +*/ + +void +conf_free_parse(conf_parse_t *raw_config); +/* Deallocates a conf_parse_t +** Parameters: (conf_parse_t *) raw_config: The raw parse to deallocate +** Returns : usually. +*/ + +void +conf_write_parse(conf_parse_t *raw_config); +/* Writes out all writeable parts of the configuration parse to the source files, if writeable +** Parameters: (conf_parse_t *) raw_config +** Effects : (stderr) Prints error message if ``modifiable'' file could not be written to +** (filesystem) All ``modifiable'' config files are overwritten +*/ + +#endif /* !defined (CONF_PARSE_H_) */ diff --git a/engines/sci/include/conf_subsystems.h b/engines/sci/include/conf_subsystems.h new file mode 100644 index 0000000000..a2cfe0c42d --- /dev/null +++ b/engines/sci/include/conf_subsystems.h @@ -0,0 +1,80 @@ +/*************************************************************************** + Copyright (C) 2007 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef CONF_SUBSYSTEMS_H_ +#define CONF_SUBSYSTEMS_H_ + +#include "conf_parse.h" +#include "conf_driver.h" + +conf_parse_t * +conf_validate(conf_main_t *main, conf_parse_t *raw_config); +/* Validates raw configuration data (including subsections) and fills in ``overridden'' and ``prev'' pointers +** Parameters: (conf_parse_t *) raw_config: The raw config data +** Returns : (conf_parse_t *) The updated raw_config on success, or NULL if validation failed +** (raw_config is not freed in that case) +** Effects : (stderr) Error message(s) if validation failed +*/ + +int +conf_init(conf_main_t *main, conf_parse_t *config, const char *id); +/* Validates all parameters, sets all parameters, loads modules, initialises drivers and subsystems and the main module +** Parameters: (conf_main_t *) The main config element +** (conf_parse_t *) The parse +** (char *) id: The game ID to choose for initialisation, or NULL for default +** Effects : (stderr) Error message(s) if validation failed +*/ + +char * +conf_set_main_option(conf_main_t *main, const char *option, const char *value); +/* Sets a value for the main module +** Parameters: (char *) option: Name of the option to set +** (char *) value: Value to set +** Returns : (char *) NULL on success, an error message otherwise +*/ + +char * +conf_set_subsystem_option(conf_main_t *main, const char *subsystem, const char *option, const char *value); +/* Sets a value for the main module +** Parameters: (char *) option: Name of the option to set +** (char *) value: Value to set +** (char *) subsystem: Name of the subsystem to manipulate +** Returns : (char *) NULL on success, an error message otherwise +*/ + +char * +conf_set_driver_option(conf_main_t *main, const char *subsystem, const char *driver, const char *option, const char *value); +/* Sets a value for the main module +** Parameters: (char *) option: Name of the option to set +** (char *) value: Value to set +** (char *) subsystem: Name of the subsystem to manipulate +** (char *) driver: Name of the driver to manipulate +** Returns : (char *) NULL on success, an error message otherwise +** Automatically loads the driver, if neccessary +*/ + +#endif /* !defined(CONF_SUBSYSTEMS_H_) */ diff --git a/engines/sci/include/conf_summary.h b/engines/sci/include/conf_summary.h new file mode 100644 index 0000000000..c2a1f8e16e --- /dev/null +++ b/engines/sci/include/conf_summary.h @@ -0,0 +1,106 @@ +/*************************************************************************** + conf_summary.h Copyright (C) 2007 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef CONF_SUMMARY_H_ +#define CONF_SUMMARY_H_ + +#include +#include + +typedef struct { + conf_option_t *option; /* NULL if this is a terminator */ + conf_parse_t *parse; /* Source location we got this information from, + ** or NULL for command-line or default */ + union { char *str; + int nr } choice; + int origin; + int flags; +} conf_option_choice_t; + +#define CONF_CHOICE_ORIGIN_DEFAULT 0 /* Built-in default */ +#define CONF_CHOICE_ORIGIN_CONFDEFAULT 1 /* Config file option */ +#define CONF_CHOICE_ORIGIN_CONFGAME 2 /* Game-specific option */ +#define CONF_CHOICE_ORIGIN_COMMANDLINE 3 + +#define CONF_CHOICE_FLAG_WRITEABLE (1<<0) /* 'parse' can be written to */ +#define CONF_CHOICE_FLAG_DISABLED (1<<1) /* Option isn't set, only listed for completeness */ + +typedef struct { + conf_option_choice_t *options; /* Driver-specific */ + conf_driver_t *driver; +} conf_driver_choice_t; + +typedef struct { + conf_option_choice_t *options; /* Subsystem-specific */ + int driver_origin; /* CONF_CHOICE_ORIGIN_* */ + conf_driver_choice_t *driver; /* The particular driver selected */ + conf_driver_choice_t *drivers; /* All available drivers, with their options */ +} conf_subsystem_choice_t; + +typedef struct { + char *game_id; /* NULL for default */ + conf_option_choice_t *options; /* Global */ + int flags; + conf_parse_t **append_location; /* Into here we can add new configuration options to override */ + + conf_subsystem_choice_t[CONF_SUBSYSTEMS_NR] subsystems; +} conf_game_choice_t; + +#define CONF_GAME_CHOICE_FLAG_OVERRIDE_IN_SECTION (1<<0) /* Override section already exists, need not be re-created */ + +typedef struct { + conf_game_choice_t *default; + + int game_choices_nr; + conf_game_choice_t *game_choices; +} conf_summary_t; + + +conf_summary_t * +conf_summary(conf_parse_t *raw_config); +/* Summarises a config parse in a conf_summary_t +** Parameters: (conf_parse_t *) raw_config: The parse to summarise +** Returns : (conf_summary_t *) A summarised configuration +** The summary includes (default) values for all driver/subsystem options +*/ + +void +conf_free_summary(conf_summary_t *summary); +/* Deallocates a configuration summary +** Parameters: (conf_summary_t *) summary: The summary to deallocate +** This only affects the summary, not the underlying parse. +*/ + +conf_game_choice_t * +conf_find_choice(conf_summary_t *choices, char *game_id); +/* Finds the correct game choice by game ID +** Parameters: (conf_summary_t *) summary: The summary to peruse +** (char *) game_id: Identified game ID. +** We search by exact match or otherwise default. +*/ + +#endif /* !defined (CONF_SUMMARY_H_) */ diff --git a/engines/sci/include/console.h b/engines/sci/include/console.h new file mode 100644 index 0000000000..fafa276298 --- /dev/null +++ b/engines/sci/include/console.h @@ -0,0 +1,254 @@ +/*************************************************************************** + console.h Copyright (C) 1999,2000,01 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + +***************************************************************************/ +/* Header file for the SCI console. +** Please note that the console does not use the priority field; the console +** should therefore be drawn after everything else has been drawn (with the +** possible exception of the mouse pointer). +*/ + +#ifndef _SCI_CONSOLE_H_ +#define _SCI_CONSOLE_H_ + +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#ifdef _DOS +#include +#endif + +#define SCI_CONSOLE +#include + + +extern DLLEXTERN int con_passthrough; +/* Echo all sciprintf() stuff to the text console */ +extern FILE *con_file; +/* Echo all sciprintf() output to a text file. Note: clients of freesci.dll +** should use open_console_file and close_console_file, rather than refer +** directly to the con_file variable. +*/ + +struct _state; /* state_t later on */ + +typedef union { + long val; + char *str; + reg_t reg; +} cmd_param_t; + +extern unsigned int cmd_paramlength; +/* The number of parameters passed to a function called from the parser */ + +extern cmd_param_t *cmd_params; +/* The parameters passed to a function called by the parser */ + +extern struct _state *con_gamestate; +/* The game state as used by some of the console commands */ + + +/*** FUNCTION DEFINITIONS ***/ + +void +con_set_string_callback(void(*callback)(char *)); +/* Sets the console string callback +** Parameters: (void -> char *) callback: The closure to invoke after +** a string for sciprintf() has been generated +** This sets a single callback function to be used after sciprintf() +** is used. +*/ + +void +con_set_pixmap_callback(void(*callback)(gfx_pixmap_t *)); +/* Sets the console pixmap callback +** Parameters: (void -> gfx_pixmap_t *) callback: The closure to invoke after +** a pixmap has been provided to be +** published in the on-screen console +** This sets a single callback function to be used after sciprintf() +** is used. +*/ + +void +con_init(void); +/* Initializes the command parser +** Parameters: (void) +** Returns : (void) +** This function will initialize hook up a few commands to the parser. +** It must be called before cmdParse() is used. +*/ + + +void +con_parse(struct _state *s, const char *command); +/* Parses a command and summons appropriate facilities to handle it +** Parameters: (state_t *) s: The state_t to use +** command: The command to execute +** Returns : (void) +*/ + + +int +con_hook_command(int command(struct _state *s), const char *name, const char *param, const char *description); +/* Adds a command to the parser's command list +** Parameters: command: The command to add +** name: The command's name +** param: A description of the parameters it takes +** description: A short description of what it does +** Returns : 0 if successful, 1 if appending failed because +** of an incorrect *param string, 'command'==0, or +** 'name' already being in use. +** A valid param string is either empty (no parameters allowed) +** or contains one of the following tokens: +** ! Special token: state_t* must be set for this function to be called +** i (an int) +** s (a 'string' (char *)) +** h (a byte, described in hexadecimal digits) +** a (a heap address, register or object name) +** r (any register value) +** x* (an arbitrary (possibly 0) number of 'x' tokens) +** The '*' token may only be used as the last token of the list. +** Another way to specify optional parameters is by means of the +** '-opt:t' notation, which allows an optional parameter of type 't' +** to be specified as 'opt:' when calling. See also the +** con_hasopt() and con_getopt() calls. +** +** Please note that the 'h' type does accept hexadecimal numbers greater +** than 0xff and less than 0x00, but clips them to this range. +** +** Example: "isi*" would define the function to take an int, a +** string, and an arbitrary number of ints as parameters (in that sequence). +** +** When the function is called, it can retrieve its parameters from cmd_params; +** the actual number of parameters is stored in cmd_paramlength. +** It is allowed to modify the char*s from a cmd_params[] element, as long +** as no element beyond strlen(cmd_params[x].str)+1 is accessed. +*/ + +cmd_param_t +con_getopt(char *opt); +/* Retreives the specified optional parameter +** -- for use within console functions only -- +** Parameters: (char *) opt: The optional parameter to retreive +** Returns : (cmd_param_t) The corresponding parameter +** Should only be used if con_hasopt() reports its presence. +*/ + +int +con_hasopt(char *opt); +/* Checks whether an optional parameter was specified +** -- for use within console functions only -- +** Parameters: (char *) opt: The optional parameter to check for +** Returns : (int) non-zero iff the parameter was specified +*/ + +int +con_can_handle_pixmaps(void); +/* Determines whether the console supports pixmap inserts +** Returns : (int) non-zero iff pixmap inserts are supported +*/ + +int +con_insert_pixmap(gfx_pixmap_t *pixmap); +/* Inserts a pixmap into the console history buffer +** Parameters: (gfx_pixmap_t *) pixmap: The pixmap to insert +** Returns : (int) 0 on success, non-zero if no receiver for +** the pixmap could not be found +** The pixmap must be unique; it is freed by the console on demand. +** Use gfx_clone_pixmap() if neccessary. +** If the pixmap could not be inserted, the called must destroy it +*/ + +int +con_hook_page(const char *topic, const char *body); +/* Hooks a general information page to the manual page system +** Parameters: (const char *) topic: The topic name +** (const char *) body: The text body to assign to the topic +** Returns : (int) 0 on success +*/ + +int +con_hook_int(int *pointer, const char *name, const char *description); +/* Adds an int to the list of modifyable ints. +** Parameters: pointer: Pointer to the int to add to the list +** name: Name for this value +** description: A short description for the value +** Returns : 0 on success, 1 if either value has already been added +** or if name is already being used for a different value. +** The internal list of int references is used by some of the basic commands. +*/ + + +void +con_gfx_init(void); +/* Initializes the gfx console +*/ + +void +con_gfx_show(gfx_state_t *state); +/* Enters on-screen console mode +** Parameters: (gfx_state_t *state): The graphics state to use for interaction +** Returns : (void) +*/ + +char * +con_gfx_read(gfx_state_t *state); +/* Reads a single line from the on-screen console, if it is open +** Parameters: (gfx_state_t *state): The graphics state to use for interaction +** Returns : (char *) The input, in a static buffer +*/ + +void +con_gfx_hide(gfx_state_t *stae); +/* Closes the on-screen console +** Parameters: (gfx_state_t *state): The graphics state to use for interaction +** Returns : (void) +*/ + + +int +sci_hexdump(byte *data, int length, int offsetplus); + +void +open_console_file(char *filename); +/* Opens the file to which the console output is echoed. If a file was opened +** before, closes it. +** Parameters: filename - name of the file +** Returns : (void) +*/ + +void +close_console_file(void); +/* Closes the console output file. +** Parameters: (void) +** Returns : (void) +*/ + +#endif /* _SCI_CONSOLE_H_ */ diff --git a/engines/sci/include/engine.h b/engines/sci/include/engine.h new file mode 100644 index 0000000000..08a26fe98b --- /dev/null +++ b/engines/sci/include/engine.h @@ -0,0 +1,306 @@ +/*************************************************************************** + engine.h Copyright (C) 1999,2000,01 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + +***************************************************************************/ +#ifndef _SCI_ENGINE_H +#define _SCI_ENGINE_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FREESCI_CURRENT_SAVEGAME_VERSION 7 +#define FREESCI_MINIMUM_SAVEGAME_VERSION 7 + +#ifdef WIN32 +# define FREESCI_GAMEDIR "FreeSCI" +# define STRLEN_FREESCI_GAMEDIR 7 +#else +# define FREESCI_GAMEDIR ".freesci" +# define STRLEN_FREESCI_GAMEDIR 8 +#endif + +#define FREESCI_CONFFILE "config" +#define FREESCI_SAVEDIR_PREFIX "save_" +#define FREESCI_CONFFILE_DOS "freesci.cfg" +#define FREESCI_GAMES_DIR "games" + +#define FREESCI_FILE_STATE "state" +#define FREESCI_ID_SUFFIX ".id" +/* Used for .id files ("real" save games) */ + +#define MAX_GAMEDIR_SIZE 32 /* Used for subdirectory inside of "~/.freesci/" */ +#define MAX_SAVEGAME_NR 20 /* Maximum number of savegames */ + +#define MAX_SAVE_DIR_SIZE MAX_HOMEDIR_SIZE + STRLEN_FREESCI_GAMEDIR + MAX_GAMEDIR_SIZE + 4 +/* +4 for the three slashes and trailing \0 */ + +/* values for state_t.restarting_flag */ +#define SCI_GAME_IS_NOT_RESTARTING 0 +#define SCI_GAME_WAS_RESTARTED 1 +#define SCI_GAME_IS_RESTARTING_NOW 2 +#define SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE 4 + +typedef struct { + int nr; + int palette; +} drawn_pic_t; + +typedef struct _state +{ + int savegame_version; + + int widget_serial_counter; /* Used for savegames */ + + char *resource_dir; /* Directory the resource files are kept in */ + char *work_dir; /* Directory the game metadata should be written to */ + resource_mgr_t *resmgr; /* The resource manager */ + + char *game_name; /* Designation of the primary object (which inherits from Game) */ + char *game_version; + + /* Non-VM information */ + + gfx_state_t *gfx_state; /* Graphics state and driver */ + gfx_pixmap_t *old_screen; /* Old screen content: Stored during kDrawPic() for kAnimate() */ + + sfx_state_t sound; /* sound subsystem */ + int sfx_init_flags; /* flags the sfx subsystem was initialised with */ + unsigned int sound_volume; /* 0x0 -> 0xf Current volume of sound system */ + unsigned int sound_mute; /* 0 = not, else == saved value */ + + byte restarting_flags; /* Flags used for restarting */ + byte have_mouse_flag; /* Do we have a hardware pointing device? */ + + byte pic_not_valid; /* Is 0 if the background picture is "valid" */ + byte pic_is_new; /* New pic was loaded or port was opened */ + byte onscreen_console; /* Use the onscreen console for debugging */ + byte *osc_backup; /* Backup of the pre-onscreen console screen data */ + + int *pic_priority_table; /* 16 entries with priorities or NULL if not present */ + + char *status_bar_text; /* Text on the status bar, or NULL if the title bar is blank */ + + int status_bar_foreground, status_bar_background; + + long game_time; /* Counted at 60 ticks per second, reset during start time */ + + reg_t save_dir_copy; /* Last copy of the save dir */ + int save_dir_edit_offset; /* For kEdit(): Display offset for editing the savedir */ + char *save_dir_copy_buf; /* Temp savedir buffer for kEdit() */ + + int mouse_pointer_view; /* Mouse pointer resource, or -1 if disabled */ + int mouse_pointer_loop; /* Mouse pointer resource, or -1 if disabled */ + int mouse_pointer_cel; /* Mouse pointer resource, or -1 if disabled */ + int save_mouse_pointer_view; /* Temporary storage for mouse pointer resource, when the pointer is hidden */ + int save_mouse_pointer_loop; /* Temporary storage for mouse pointer resource, when the pointer is hidden */ + int save_mouse_pointer_cel; /* Temporary storage for mouse pointer resource, when the pointer is hidden */ + + int port_serial; /* Port serial number, for save/restore */ + gfxw_port_t *port; /* The currently active port */ + + gfx_color_t ega_colors[16]; /* The 16 EGA colors- for SCI0(1) */ + + gfxw_visual_t *visual; /* A visual widget, containing all ports */ + + gfxw_port_t *titlebar_port; /* Title bar viewport (0,0,9,319) */ + gfxw_port_t *wm_port; /* window manager viewport and designated &heap[0] view (10,0,199,319) */ + gfxw_port_t *picture_port; /* The background picture viewport (10,0,199,319) */ + gfxw_port_t *iconbar_port; /* Full-screen port used for non-clipped icon bar draw in SCI1 */ + + gfx_map_mask_t pic_visible_map; /* The number of the map to display in update commands */ + int pic_animate; /* The animation used by Animate() to display the picture */ + + int dyn_views_list_serial; /* Used for save/restore */ + gfxw_list_t *dyn_views; /* Pointers to pic and dynamic view lists */ + + int drop_views_list_serial; /* Used for save/restore */ + gfxw_list_t *drop_views; /* A list Animate() can dump dropped dynviews into */ + + long animation_delay; /* A delay factor for pic opening animations. Defaults to 500. */ + int animation_granularity; /* Number of animation steps to perform betwen updates for transition animations */ + + menubar_t *menubar; /* The menu bar */ + + int priority_first; /* The line where priority zone 0 ends */ + int priority_last; /* The line where the highest priority zone starts */ + + int pics_drawn_nr; + int pics_nr; + drawn_pic_t *pics; + + GTimeVal game_start_time; /* The time at which the interpreter was started */ + GTimeVal last_wait_time; /* The last time the game invoked Wait() */ + + byte version_lock_flag; /* Set to 1 to disable any autodetection mechanisms */ + sci_version_t version; /* The approximated patchlevel of the version to emulate */ + sci_version_t max_version, min_version; /* Used for autodetect sanity checks */ + + unsigned int kernel_opt_flags; /* Kernel optimization flags- used for performance tweaking */ + + /* Kernel File IO stuff */ + + int file_handles_nr; /* maximum numer of allowed file handles */ + FILE **file_handles; /* Array of file handles. Dynamically increased if required. */ + + reg_t dirseeker_outbuffer; + sci_dir_t dirseeker; + + /* VM Information */ + + exec_stack_t *execution_stack; /* The execution stack */ + int execution_stack_size; /* Number of stack frames allocated */ + int execution_stack_pos; /* Position on the execution stack */ + int execution_stack_base; /* When called from kernel functions, the vm + ** is re-started recursively on the same stack. + ** This variable contains the stack base for the + ** current vm. + */ + int execution_stack_pos_changed; /* Set to 1 if the execution stack position + ** should be re-evaluated by the vm + */ + + reg_t r_acc; /* Accumulator */ + unsigned int r_amp_rest; /* &rest register (only used for save games) */ + reg_t r_prev; /* previous comparison result */ + + seg_id_t stack_segment; /* Heap area for the stack to use */ + stack_ptr_t stack_base; /* Pointer to the least stack element */ + stack_ptr_t stack_top; /* First invalid stack element */ + + seg_id_t parser_segment; /* A heap area used by the parser for error reporting */ + reg_t parser_base; /* Base address for the parser error reporting mechanism */ + reg_t parser_event; /* The event passed to Parse() and later used by Said() */ + seg_id_t script_000_segment; + script_t *script_000; /* script 000, e.g. for globals */ + + int parser_lastmatch_word; /* Position of the input word the parser last matched on, or SAID_NO_MATCH */ + + /* Debugger data: */ + breakpoint_t *bp_list; /* List of breakpoints */ + int have_bp; /* Bit mask specifying which types of breakpoints are used in bp_list */ + unsigned int debug_mode; /* Contains flags for the various debug modes */ + + /* System strings */ + seg_id_t sys_strings_segment; + sys_strings_t *sys_strings; + + /* Parser data: */ + word_t **parser_words; + int parser_words_nr; + suffix_t **parser_suffices; + int parser_suffices_nr; + parse_tree_branch_t *parser_branches; + parse_rule_list_t *parser_rules; /* GNF rules used in the parser algorithm */ + int parser_branches_nr; + parse_tree_node_t parser_nodes[VOCAB_TREE_NODES]; /* The parse tree */ + + int parser_valid; /* If something has been correctly parsed */ + + synonym_t *synonyms; /* The list of synonyms */ + int synonyms_nr; + + reg_t game_obj; /* Pointer to the game object */ + + int classtable_size; /* Number of classes in the table- for debugging */ + class_t *classtable; /* Table of all classes */ + + seg_manager_t seg_manager; + int gc_countdown; /* Number of kernel calls until next gc */ + + int selector_names_nr; /* Number of selector names */ + char **selector_names; /* Zero-terminated selector name list */ + int kernel_names_nr; /* Number of kernel function names */ + char **kernel_names; /* List of kernel names */ + + kfunct_sig_pair_t *kfunct_table; /* Table of kernel functions */ + int kfunct_nr; /* Number of mapped kernel functions; may be more than + ** kernel_names_nr */ + + opcode *opcodes; + + selector_map_t selector_map; /* Shortcut list for important selectors */ + + /* Backwards compatibility crap */ + int port_ID; + + struct _state *successor; /* Successor of this state: Used for restoring */ + +} state_t; + + +#define STATE_T_DEFINED + +int +gamestate_save(state_t *s, char *dirname); +/* Saves a game state to the hard disk in a portable way +** Parameters: (state_t *) s: The state to save +** (char *) dirname: The subdirectory to store it in +** Returns : (int) 0 on success, 1 otherwise +*/ + +state_t * +gamestate_restore(state_t *s, char *dirname); +/* Restores a game state from a directory +** Parameters: (state_t *) s: An older state from the same game +** (char *) dirname: The subdirectory to restore from +** Returns : (state_t *) NULL on failure, a pointer to a valid state_t otherwise +*/ + +gfx_pixmap_color_t * +get_pic_color(state_t *s, int color); +/* Retrieves the gfx_pixmap_color_t associated with a game color index +** Parameters: (state_t *) s: The game state +** (int) color: The color to look up +** Returns : (gfx_pixmap_color_t *) The requested color. +*/ + +void +other_libs_exit(void); +/* Called directly before FreeSCI ends to allow libraries to clean up +*/ + +static inline +reg_t not_register(state_t *s, reg_t r) +{ + if (s->version >= SCI_VERSION_FTU_INVERSE_CANBEHERE) + return make_reg(0, !r.offset); else + return r; + +} + +#endif /* !_SCI_ENGINE_H */ diff --git a/engines/sci/include/event.h b/engines/sci/include/event.h new file mode 100644 index 0000000000..aea54b1af2 --- /dev/null +++ b/engines/sci/include/event.h @@ -0,0 +1,15 @@ +#ifndef EVENT_H +#define EVENT_H + +#include + +struct _state; + +sci_event_t getEvent (struct _state *s); +/* Returns the next SCI_EV_* event +** Parameters: (struct state *) Current game state +** Returns : (sci_event_t) The next event, which may be any of the +** existing events. +*/ + +#endif diff --git a/engines/sci/include/game_select.h b/engines/sci/include/game_select.h new file mode 100644 index 0000000000..91b3d957f6 --- /dev/null +++ b/engines/sci/include/game_select.h @@ -0,0 +1,38 @@ +#include "list.h" + +#ifndef _SCI_GAME_SELECT_H +#define _SCI_GAME_SELECT_H + +typedef struct game +{ + char *name; + char dir[PATH_MAX]; + int conf_nr; +} game_t; + +typedef struct games_list +{ + LIST_ENTRY(games_list) entries; + + game_t game; +} games_list_t; + +typedef LIST_HEAD(games_list_head, games_list) games_list_head_t; + +int game_select_gfxop_init_default(gfx_state_t *state, gfx_options_t *options, void *misc_info); + +int game_select_gfxop_init(gfx_state_t *state, int xfact, int yfact, gfx_color_mode_t bpp, gfx_options_t *options, void *misc_info); + +int +game_select_display(gfx_driver_t *gfx_driver, game_t* game_list, int game_count, gfx_bitmap_font_t* font_default, gfx_bitmap_font_t* font_small); + +void game_select_scan_info(gfx_driver_t *gfx_driver, gfx_bitmap_font_t* font_default, gfx_bitmap_font_t* font_small, char *name, int total); + + +#if 0 +/* this can be used to generate code that creates a particular font at runtime */ +/* this is meant to be used as a development tool */ +void save_font(int id, gfx_bitmap_font_t* font) +#endif + +#endif /* _SCI_GAME_SELECT_H */ diff --git a/engines/sci/include/gfx_driver.h b/engines/sci/include/gfx_driver.h new file mode 100644 index 0000000000..9903208988 --- /dev/null +++ b/engines/sci/include/gfx_driver.h @@ -0,0 +1,438 @@ +/*************************************************************************** + gfx_driver.h Copyright (C) 2000 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef _SCI_GFX_DRIVER_H_ +#define _SCI_GFX_DRIVER_H_ + +#include +#include + + +typedef enum { + GFX_BUFFER_FRONT = 0, + GFX_BUFFER_BACK = 1, + GFX_BUFFER_STATIC = 2 +} gfx_buffer_t; + + +/* graphics driver hints */ +#define GFX_CAPABILITY_SHADING (1<<0) +#define GFX_CAPABILITY_MOUSE_POINTER (1<<1) +#define GFX_CAPABILITY_COLOR_MOUSE_POINTER (1<<2) +#define GFX_CAPABILITY_PIXMAP_REGISTRY (1<<3) +#define GFX_CAPABILITY_SCALEABLE_PIXMAPS (1<<4) +#define GFX_CAPABILITY_STIPPLED_LINES (1<<6) +#define GFX_CAPABILITY_MOUSE_SUPPORT (1<<7) +#define GFX_CAPABILITY_POINTER_PIXMAP_REGISTRY (1<<8) +#define GFX_CAPABILITY_FINE_LINES (1<<9) +#define GFX_CAPABILITY_WINDOWED (1<<10) +#define GFX_CAPABILITY_KEYTRANSLATE (1<<11) + +#define GFX_DEBUG_POINTER (1<<0) +#define GFX_DEBUG_UPDATES (1<<1) +#define GFX_DEBUG_PIXMAPS (1<<2) +#define GFX_DEBUG_BASIC (1<<3) /* Basic geometric ops (lines, boxes, etc) */ + +/* Principial graphics driver architecture +** --------------------------------------- +** +** All graphics drivers must provide +** - One visual front buffer (the actually visible thing) +** - Two dynamic back buffers: +** + visual +** + priority +** - Two static buffers (containing the background image and picviews): +** + visual +** + priority +** +** The control buffer is handled outside the graphics driver architecture. +** Graphics are drawn by first setting the static buffers, then updating +** the back buffers (from the static buffers), adding all picviews and other +** widgets, and finally updating the front buffer. +** +** All coordinates refer to the scaled coordinate system. +** Invalid parameters should produce an error message. +** Support for some valid parameter values is optional (like different line +** modes). If an unsupported but valid parameter is specified, the function +** must use a reasonable default value. +*/ + +#define SCI_GFX_DRIVER_VERSION 0 +#define SCI_GFX_DRIVER_MAGIC 0xf001337 + +typedef struct _gfx_driver { /* Graphics driver */ + + const char *name; /* Graphics driver name. Unique identifier, should consist of + ** lower-case (where applicable) alphanumerics + */ + + const char *version; /* Free-form version description (for informative purposes + ** only) + */ + + int sci_driver_magic; /* SCI_GFX_DRIVER_MAGIC */ + int sci_driver_version; /* SCI_GFX_DRIVER_VERSION */ + + gfx_mode_t *mode; /* Currently active mode, NULL if no mode is active */ + + int pointer_x, pointer_y; /* Mouse pointer position */ + + int capabilities; /* The driver's capabilities: A list of flags that may + ** be pre-defined or set after a successful initialization. + */ + /* Capability flags: + ** + ** The words MUST, SHOULD and MAY are to be interpreted as described in + ** the IETF RFC 1123. + ** + ** GFX_CAPABILITY_SHADING: draw_filled_rect() supports drawing shaded + ** rectangles. + ** GFX_CAPABILITY_MOUSE_POINTER: The driver has built-in support for mouse + ** pointers (monochrome or colored). + ** GFX_CAPABILITY_COLOR_MOUSE_POINTER: The driver has built-in support for + ** colored mouse pointers. + ** GFX_CAPABILITY_PIXMAP_REGISTRY: System provides a pixmap registry. The + ** invoking functions will assume that all pixmaps MUST be registered; + ** if this flag is not set, it assumes that pixmaps MUST NOT be + ** registered. Note that this excludes pointer pixmaps (see below) + ** GFX_CAPABILITY_POINTER_PIXMAP_REGISTRY: The system provides a pixmap + ** registry which mouse pointers have to be registered in explicitly. + ** This MUST be used only if the registry is identical to the 'normal' pixmap + ** registry. Otherwise, it MUST be handled manually by the driver, + ** unless pointer support is disabled completely. + ** GFX_CAPABILITY_SCALEABLE_PIXMAPS: Pixmap scaling is fully supported. + ** If this capability is flag is set, all pixmaps passed to the driver + ** will be unscaled. + ** GFX_CAPABILITY_STIPPLED_LINES: The driver is able to draw stippled lines + ** horizontally and vertically (xl = 0 or yl = 0). + ** GFX_CAPABILITY_MOUSE_SUPPORT: There is some support for mouse (or similar) + ** input. Note that this flag may be disabled by external code after + ** initialization time, if no support for mouse pointer /drawing/ is + ** available. + ** GFX_CAPABILITY_FINE_LINES: Should be set IFF the driver supports drawing + ** fine (width 1) lines + ** GFX_CAPABILITY_WINDOWED: Driver runs in a window and supports a debug + ** console running on stdin/stdout + ** GFX_CAPABILITY_KEYTRANSLATE: The driver's input layer automatically + ** handles 'shifted' keys (i.e. turning shift-'a' to 'A' etc.). + ** Drivers only need to handle this if they desire to support + ** non-US keyboard layouts, usually by localisation methods + ** provided by the underlying windowing or operating system. + */ + + unsigned int debug_flags; /* Driver debug flags */ + + + /*** Initialization ***/ + + int (*set_parameter) (struct _gfx_driver *drv, char *attribute, char *value); + /* Sets a driver-specific parameter + ** Parameters: (gfx_driver_t *) drv: Pointer to the affected driver + ** (char *) attribute: Name of the attribute/parameter to set + ** (char *) value: The value to set, or NULL to query the value + ** Returns : (int) GFX_OK or GFX_FATAL, which signals a fatal error + ** condition. + ** This function should make extensive use of sciprintf() to signal invalid + ** values or unapplicable attributes. + ** Note that it may be called either before initialization (to interpret + ** config file or command line parameters) or afterwars (from the command + ** console). + */ + + int (*init_specific) (struct _gfx_driver *drv, int xres, int yres, + int bytespp); + /* Attempts to initialize a specific graphics mode + ** Parameters: (gfx_driver_t *) drv: The affected driver + ** (int x int) xres, yres: Horizontal and vertical scaling + ** factors + ** (int) bytespp: Any of GFX_COLOR_MODE_*. GFX_COLOR_MODE_INDEX + ** implies color index mode. + ** Returns : (int) GFX_OK on success, GFX_ERROR if the mode could not be + ** set, or GFX_FATAL if the graphics target is unuseable. + ** The scaling factors apply to the standard SCI resolution of 320x200 pixels + ** and is used for internal representation of graphical data. The physical + ** resolution set by the graphics driver may be different for practical + ** reasons. + ** Must also set drv->mode, preferably with the gfx_new_mode() function + ** specified in gfx_tools.h. + */ + + int (*init) (struct _gfx_driver *drv); + /* Initialize any graphics mode + ** Parameters: (gfx_driver_t *) drv: The affected driver + ** Returns : (int) GFX_OK on success, GFX_FATAL otherwise. + ** This function attempts to set /any/ graphics mode, starting with the one + ** most 'natural' to the graphics target. Target implementors have relatively + ** free reign in choosing the heuristics used to determine the resulting + ** mode. + ** Must also set drv->mode, preferably with the gfx_new_mode() function + ** specified in gfx_tools.h. + */ + + void (*exit) (struct _gfx_driver *drv); + /* Uninitializes the current graphics mode + ** Paramters: (gfx_driver_t *) drv: The driver to uninitialize + ** Return : (void) + ** This function frees all memory allocated by the graphics driver, + ** including mode and palette information, uninstalls all console commands + ** introduced by preceeding init() or init_specific() commands, and does any + ** clean-up work (like closing visuals or returning to text mode) required by + ** the graphics infrastructure used. + */ + + + /*** Drawing operations ***/ + + int (*draw_line) (struct _gfx_driver *drv, + point_t start, point_t end, + gfx_color_t color, + gfx_line_mode_t line_mode, gfx_line_style_t line_style); + /* Draws a single line to the back buffer. + ** Parameters: (gfx_driver_t *) drv: The driver affected + ** (point_t) start: Starting point of the line to draw + ** (point_t) end: End point of the line to draw + ** (gfx_color_t *) color: The color to draw with + ** (int) line_mode: Any of the line modes + ** (int) line_style: Any of the line styles + ** Returns : (int) GFX_OK or GFX_FATAL + ** Note that color.priority is relevant and must be drawn if + ** (color.mask & GFX_MASK_PRIORITY). + ** Support for line modes other than GFX_LINE_MODE_FAST is optional. + ** For non-fine lines, the coordinates provided describe the upper left + ** corner of the pixels of the line to draw. + ** line_style support is optional, if GFX_CAPABILITY_STIPPLED_LINES is not + ** set. + */ + + int (*draw_filled_rect) (struct _gfx_driver *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode); + /* Draws a single filled and possibly shaded rectangle to the back buffer. + ** Parameters: (gfx_driver_t *) drv: The driver affected + ** (rect_t *) rect: The rectangle to draw + ** (gfx_color_t *) color1, color2: The colors to draw with + ** (int) shade_mode: Any of GFX_SHADE_*. + ** Returns : (int) GFX_OK or GFX_FATAL + ** Note that color.priority is relevant and must be drawn if + ** (color.mask & GFX_MASK_PRIORITY). + ** color2 is relevant only if shade_mode is not GFX_SHADE_FLAT. + ** Support for shade modes other than GFX_SHADE_FLAT is optional. + */ + + /*** Pixmap operations ***/ + + int (*register_pixmap) (struct _gfx_driver *drv, gfx_pixmap_t *pxm); + /* Registers a pixmap with the driver. + ** Parameters: (gfx_driver_t *) drv: The driver + ** (gfx_pixmap_t *) pxm: The pixmap to register + ** Returns : GFX_OK or GFX_FATAL + ** This function may be NULL if GFX_CAPABILITY_PIXMAP_REGISTRY is not + ** set. + ** pxm->internal may be used to store any handle or meta information. + */ + + int (*unregister_pixmap) (struct _gfx_driver *drv, gfx_pixmap_t *pxm); + /* Unregisters a pixmap previously registered with register_pixmap() + ** Parameters: (gfx_driver_t *) drv: The driver + ** (gfx_pixmap_t *) pxm: The pixmap to register + ** Returns : (int) GFX_OK or GFX_FATAL, or GFX_ERROR if pxm was + ** not registered + ** Just like register_pixmap(), this function may be NULL unless + ** GFX_CAPABILITY_PIXMAP_REGISTRY is set. + */ + + int (*draw_pixmap) (struct _gfx_driver *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer); + /* Draws part of a pixmap to the static or back buffer + ** Parameters: (gfx_driver_t *) drv: The affected driver + ** (gfx_pixmap_t *) pxm: The pixmap to draw + ** (int) priority: The priority to draw with, or GFX_NO_PRIORITY + ** to draw on top of everything without setting the + ** priority back buffer + ** (rect_t) src: The pixmap-relative source rectangle + ** (rect_t) dest: The destination rectangle + ** (int) buffer: One of GFX_BUFFER_STATIC and GFX_BUFFER_BACK + ** Returns : (int) GFX_OK or GFX_FATAL, or GFX_ERROR if pxm was not + ** (but should have been) registered. + ** dest.xl and dest.yl must be evaluated and used for scaling if + ** GFX_CAPABILITY_SCALEABLE_PIXMAPS is supported. + */ + + int (*grab_pixmap) (struct _gfx_driver *drv, rect_t src, gfx_pixmap_t *pxm, + gfx_map_mask_t map); + /* Grabs an image from the visual or priority back buffer + ** Parameters: (gfx_driver_t *) drv: The affected driver + ** (rect_t) src: The rectangle to grab + ** (gfx_pixmap_t *) pxm: The pixmap structure the data is to + ** be written to + ** (int) map: GFX_MASK_VISUAL or GFX_MASK_PRIORITY + ** Returns : (int) GFX_OK, GFX_FATAL, or GFX_ERROR for invalid map values + ** pxm may be assumed to be empty and pre-allocated with an appropriate + ** memory size. + ** This function is now mandatory. + */ + + + /*** Buffer operations ***/ + + int (*update) (struct _gfx_driver *drv, rect_t src, point_t dest, + gfx_buffer_t buffer); + /* Updates the front buffer or the back buffers + ** Parameters: (gfx_driver_t *) drv: The affected driver + ** (rect_t) src: Source rectangle + ** (point_t) dest: Destination point + ** (int) buffer: One of GFX_BUFFER_FRONT or GFX_BUFFER_BACK + ** Returns : (int) GFX_OK, GFX_ERROR or GFX_FATAL + ** This function updates either the visual front buffer, or the two back + ** buffers, by copying the specified source region to the destination + ** region. + ** For heuristical reasons, it may be assumed that the x and y fields of + ** src and dest will be identical in /most/ cases. + ** If they aren't, the priority map will not be required to be copied. + */ + + int (*set_static_buffer) (struct _gfx_driver *drv, gfx_pixmap_t *pic, + gfx_pixmap_t *priority); + /* Sets the contents of the static visual and priority buffers + ** Parameters: (gfx_driver_t *) drv: The affected driver + ** (gfx_pixmap_t *) pic: The image defining the new content + ** of the visual back buffer + ** (gfx_pixmap_t *) priority: The priority map containing + ** the new content of the priority back buffer + ** in the index buffer + ** Returns : (int) GFX_OK or GFX_FATAL + ** pic and priority may be modified or written to freely. They may also be + ** used as the actual static buffers, since they are not freed and re- + ** allocated between calls to set_static_buffer() and update(), unless + ** exit() was called in between. + ** Note that later version of the driver interface may disallow modifying + ** pic and priority. + ** pic and priority are always scaled to the appropriate resolution, even + ** if GFX_CAPABILITY_SCALEABLE_PIXMAPS is set. + */ + + + /*** Mouse pointer operations ***/ + + int (*set_pointer) (struct _gfx_driver *drv, gfx_pixmap_t *pointer); + /* Sets a new mouse pointer. + ** Parameters: (gfx_driver_t *) drv: The driver to modify + ** (gfx_pixmap_t *) pointer: The pointer to set, or NULL to set + ** no pointer + ** Returns : (int) GFX_OK or GFX_FATAL + ** This function may be NULL if GFX_CAPABILITY_MOUSE_POINTER is not set. + ** If pointer is not NULL, it will have been scaled to the appropriate + ** size and registered as a pixmap (if neccessary) beforehand. + ** If this function is called for a target that supports only two-color + ** pointers, the image is a color index image, where only color index values + ** 0, 1, and GFX_COLOR_INDEX_TRANSPARENT are used. + */ + + + /*** Palette operations ***/ + + int (*set_palette) (struct _gfx_driver *drv, int index, byte red, byte green, + byte blue); + /* Manipulates a palette index in the hardware palette + ** Parameters: (gfx_driver_t *) drv: The driver affected + ** (int) index: The index of the palette entry to modify + ** (int x int x int) red, green, blue: The RGB intensities to + ** set for the specified index. The minimum + ** intensity is 0, maximum is 0xff. + ** Returns : (int) GFX_OK, GFX_ERROR or GFX_FATAL + ** This function does not need to update mode->palette, as this is done + ** by the calling code. + ** set_palette() is only required for targets supporting color index mode. + */ + + + /*** Event management ***/ + + sci_event_t (*get_event) (struct _gfx_driver *drv); + /* Returns the next event in the event queue for this driver + ** Parameters: (gfx_driver_t *) drv: The driver to query + ** Returns : (sci_event_t) The oldest event still in the driver's event + ** queue, or the null event if there is none. + */ + + int (*usec_sleep) (struct _gfx_driver *drv, long usecs); + /* Sleeps the specified amount of microseconds, or until the mouse moves + ** Parameters: (gfx_driver_t *) drv: The relevant driver + ** (long) usecs: Amount of microseconds to sleep + ** Returns : (int) GFX_OK or GFX_FATAL + ** This function returns when the specified amount of microseconds has + ** elapsed, or when the mouse pointer has been moved and needs to be redrawn. + ** Only targets that can handle colored mouse pointers may choose to handle + ** all mouse management internally. + */ + + void *state; /* Reserved for internal use */ + +} gfx_driver_t; + + + +gfx_driver_t * +gfx_find_driver(char *, char *name); +/* Attempts to match a graphics driver to a name +** Parameters: (char *) path: The path to search in +** (char *) name: The name of the graphics driver to look for +** or NULL for the default driver +** Returns : (gfx_driver_t *) The resulting driver, or NULL if none +** was found +*/ + +const char * +gfx_get_driver_name(int nr); +/* Retreives the name of the driver with the specified number +** Parameters: (int) nr: Number of the driver +** (char *) The driver's name +** Note that this function only makes sense within a loop or if nr=0, since +** the result value is valid iff nr >= 0 AND there does not exist an nr' +** with 0 <= nr' < nr so that gfx_get_driver_name(nr') == NULL. +*/ + +/*** Utility functions for set_parameter implementations */ + +int +string_truep(char *value); +/* Tests whether a string expresses truth +** Parameters: (char *) value: The value to test +** Returns : non-zero iff 'value' contans a string expressing something +** along the lines of "yes" +*/ + +int +string_falsep(char *value); +/* Tests whether a string expresses falsehood +** Parameters: (char *) value: The value to test +** Returns : non-zero iff 'value' contans a string expressing something +** along the lines of "no" +*/ + + + +#endif /* !_SCI_GFX_DRIVER_H_ */ diff --git a/engines/sci/include/gfx_operations.h b/engines/sci/include/gfx_operations.h new file mode 100644 index 0000000000..6e94523da8 --- /dev/null +++ b/engines/sci/include/gfx_operations.h @@ -0,0 +1,733 @@ +/*************************************************************************** + gfx_operations.h Copyright (C) 2000,01 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Graphical operations, called from the widget state manager */ + +#ifndef _GFX_OPERATIONS_H_ +#define _GFX_OPERATIONS_H_ + +#include +#include +#include +#include +#include + +#define GFXOP_NO_POINTER -1 + +/* Threshold in color index mode to differentiate between visible and non-visible stuff. +** GFXOP_ALPHA_THRESHOLD itself should be treated as non-visible. +*/ +#define GFXOP_ALPHA_THRESHOLD 0xff + +typedef struct { + char *text; /* Copy of the actual text */ + + int lines_nr; + int line_height; + text_fragment_t *lines; /* Text offsets */ + gfx_bitmap_font_t *font; + gfx_pixmap_t **text_pixmaps; + + int width, height; + + int priority, control; + gfx_alignment_t halign, valign; +} gfx_text_handle_t; + +/* Unless individually stated otherwise, the following applies: +** All operations herein apply to the standard 320x200 coordinate system. +** All operations perform clipping relative to state->clip_zone. +*/ + +typedef enum { + GFX_BOX_SHADE_FLAT, + GFX_BOX_SHADE_RIGHT, + GFX_BOX_SHADE_LEFT, + GFX_BOX_SHADE_DOWN, + GFX_BOX_SHADE_UP +#if 0 + /* possible with alphaing, but there is no way to check for + ** alpha capability of gfx_driver->draw_filled_rect() yet + */ + ,GFX_BOX_SHADE_RIGHT_DOWN, + GFX_BOX_SHADE_LEFT_DOWN, + GFX_BOX_SHADE_RIGHT_UP, + GFX_BOX_SHADE_LEFT_UP +#endif +} gfx_box_shade_t; + + +typedef struct _dirty_rect { + rect_t rect; + struct _dirty_rect *next; +} gfx_dirty_rect_t; + + +typedef struct _gfx_event { + sci_event_t event; + struct _gfx_event *next; +} gfx_input_event_t; + +typedef struct { + int version; /* Interpreter version */ + + gfx_options_t *options; + + point_t pointer_pos; /* Mouse pointer coordinates */ + + rect_t clip_zone_unscaled; /* The current UNSCALED clipping zone */ + rect_t clip_zone; /* The current SCALED clipping zone; a cached scaled version of clip_zone_unscaled */ + + gfx_driver_t *driver; + gfx_pixmap_color_t *static_palette; /* Null for dynamic palettes */ + int static_palette_entries; + + int visible_map; + + gfx_resstate_t *resstate; /* Resource state */ + + gfx_pixmap_t *priority_map; /* back buffer priority map (unscaled) */ + gfx_pixmap_t *static_priority_map; /* static buffer priority map (unscaled) */ + gfx_pixmap_t *control_map; /* back buffer control map (only exists unscaled in the first place) */ + + + int mouse_pointer_visible; /* Whether the pointer is drawn right now */ + point_t old_pointer_draw_pos; /* Mouse pointer draw coordinates */ + rect_t pointer_bg_zone; /* old-pointer-draw-pos relative zone inside the pointer + ** pixmap that was drawn */ + + int mouse_pointer_in_hw; /* Current pointer is being handled in hardware */ + + gfx_pixmap_t *mouse_pointer; /* Only set when drawing the mouse manually */ + gfx_pixmap_t *mouse_pointer_bg; /* Background under the pointer */ + + int tag_mode; /* Set to 1 after a new pic is drawn and the resource manager + ** has tagged all resources. Reset after the next front buffer + ** update is done, when all resources that are still tagged are + ** flushed. */ + + int disable_dirty; /* Set to 1 to disable dirty rect accounting */ + + int pic_nr; /* Number of the current pic */ + int palette_nr; /* Palette number of the current pic */ + + gfx_input_event_t *events; + + gfx_pixmap_t *fullscreen_override; /* An optional override picture which must have unscaled + ** full-screen size, which overrides all other visibility, and + ** which is generally slow */ + + gfxr_pic_t *pic, *pic_unscaled; /* The background picture and its unscaled equivalent */ + + struct _dirty_rect *dirty_rects; /* Dirty rectangles */ + + void *internal_state; /* Internal interpreter information */ + +} gfx_state_t; + + +/**************************/ +/* Fundamental operations */ +/**************************/ + +int +gfxop_init_default(gfx_state_t *state, gfx_options_t *options, void *misc_info); +/* Initializes a graphics mode suggested by the graphics driver +** Parameters: (gfx_state_ t *) state: The state to initialize in that mode +** (gfx_options_t *) options: Rendering options +** (void *) misc_info: Additional information for the interpreter +** part of the resource loader +** Returns : (int) GFX_OK on success, GFX_FATAL otherwise +*/ + +int +gfxop_init(gfx_state_t *state, int xfact, int yfact, gfx_color_mode_t bpp, + gfx_options_t *options, void *misc_info); +/* Initializes a custom graphics mode +** Parameters: (gfx_state_t *) state: The state to initialize +** (int x int) xfact, yfact: Horizontal and vertical scale factors +** (gfx_color_mode_t) bpp: Bytes per pixel to initialize with, or +** 0 (GFX_COLOR_MODE_AUTO) to auto-detect +** (gfx_options_t *) options: Rendering options +** (void *) misc_info: Additional information for the interpreter +** part of the resource loader +** Returns : (int) GFX_OK on success, GFX_ERROR if that particular mode is +** unavailable, or GFX_FATAL if the graphics driver is unable +** to provide any useful graphics support +*/ + +int +gfxop_set_parameter(gfx_state_t *state, char *attribute, char *value); +/* Sets a driver-specific parameter +** Parameters: (gfx_state_t *) state: The state, encapsulating the driver object to manipulate +** (char *) attribute: The attribute to set +** (char *) value: The value the attribute should be set to +** Returns : (int) GFX_OK on success, GFX_FATAL on fatal error conditions triggered +** by the command +*/ + +int +gfxop_exit(gfx_state_t *state); +/* Deinitializes a currently active driver +** Parameters: (gfx_state_t *) state: The state encapsulating the driver in question +** Returns : (int) GFX_OK +*/ + +int +gfxop_scan_bitmask(gfx_state_t *state, rect_t area, gfx_map_mask_t map); +/* Calculates a bit mask calculated from some pixels on the specified map +** Parameters: (gfx_state_t *) state: The state containing the pixels to scan +** (rect_t) area: The area to check +** (gfx_map_mask_t) map: The GFX_MASKed map(s) to test +** Returns : (int) An integer value where, for each 0<=i<=15, bit #i is set +** iff there exists a map for which the corresponding bit was set +** in the 'map' parameter and for which there exists a pixel within +** the specified area so that the pixel's lower 4 bits, interpreted +** as an integer value, equal i. +** (Short version: This is an implementation of "on_control()"). +*/ + +int +gfxop_set_visible_map(gfx_state_t *state, gfx_map_mask_t map); +/* Sets the currently visible map +** Parameters: (gfx_state_t *) state: The state to modify +** (gfx_map_mask_t) map: The GFX_MASK to set +** Returns : (int) GFX_OK, or GFX_ERROR if map was invalid +** 'visible_map' can be any of GFX_MASK_VISUAL, GFX_MASK_PRIORITY and GFX_MASK_CONTROL; the appropriate +** map (as far as its contents are known to the graphics subsystem) is then subsequently drawn to the +** screen at each update. If this is set to anything other than GFX_MASK_VISUAL, slow full-screen updates +** are performed. Mostly useful for debugging. +** The screen needs to be updated for the changes to take effect. +*/ + +int +gfxop_set_clip_zone(gfx_state_t *state, rect_t zone); +/* Sets a new clipping zone +** Parameters: (gfx_state_t *) state: The affected state +** (rect_t) zone: The new clipping zone +** Returns : (int) GFX_OK +*/ + +int +gfxop_have_mouse(gfx_state_t *state); +/* Determines whether a pointing device is attached +** Parameters: (gfx_state_t *) state: The state to inspect +** Returns : (int) zero iff no pointing device is attached +*/ + + +/******************************/ +/* Generic drawing operations */ +/******************************/ + +int +gfxop_draw_line(gfx_state_t *state, + point_t start, point_t end, + gfx_color_t color, gfx_line_mode_t line_mode, + gfx_line_style_t line_style); +/* Renders a clipped line to the back buffer +** Parameters: (gfx_state_t *) state: The state affected +** (point_t) start: Starting point of the line +** (point_t) end: End point of the line +** (gfx_color_t) color: The color to use for drawing +** (gfx_line_mode_t) line_mode: Any valid line mode to use +** (gfx_line_style_t) line_style: The line style to use +** Returns : (int) GFX_OK or GFX_FATAL +*/ + +int +gfxop_draw_rectangle(gfx_state_t *state, rect_t rect, gfx_color_t color, gfx_line_mode_t line_mode, + gfx_line_style_t line_style); +/* Draws a non-filled rectangular box to the back buffer +** Parameters: (gfx_state_t *) state: The affected state +** (rect_t) rect: The rectangular area the box is drawn to +** (gfx_color_t) color: The color the box is to be drawn in +** (gfx_line_mode_t) line_mode: The line mode to use +** (gfx_line_style_t) line_style: The line style to use for the box +** Returns : (int) GFX_OK or GFX_FATAL +** Boxes drawn in thin lines will surround the minimal area described by rect. +*/ + +int +gfxop_draw_box(gfx_state_t *state, rect_t box, gfx_color_t color1, gfx_color_t color2, + gfx_box_shade_t shade_type); +/* Draws a filled box to the back buffer +** Parameters: (gfx_state_t *) state: The affected state +** (rect_t) box: The area to draw to +** (gfx_color_t) color1: The primary color to use for drawing +** (gfx_color_t) color2: The secondary color to draw in +** (gfx_box_shade_t) shade_type: The shading system to use +** (e.g. GFX_BOX_SHADE_FLAT) +** Returns : (int) GFX_OK or GFX_FATAL +** The draw mask, control, and priority values are derived from color1. +*/ + +int +gfxop_fill_box(gfx_state_t *state, rect_t box, gfx_color_t color); +/* Fills a box in the back buffer with a specific color +** Parameters: (gfx_state_t *) state: The state to draw to +** (rect_t) box: The box to fill +** (gfx_color_t) color: The color to use for filling +** Returns : (int) GFX_OK or GFX_FATAL +** This is a simple wrapper function for gfxop_draw_box +*/ + +int +gfxop_clear_box(gfx_state_t *state, rect_t box); +/* Copies a box from the static buffer to the back buffer +** Parameters: (gfx_state_t *) state: The affected state +** (rect_t) box: The box to propagate from the static buffer +** Returns : (int) GFX_OK or GFX_FATAL +*/ + + +int +gfxop_update(gfx_state_t *state); +/* Updates all dirty rectangles +** Parameters: (gfx_state_t) *state: The relevant state +** Returns : (int) GFX_OK or GFX_FATAL if reported by the driver +** In order to track dirty rectangles, they must be enabled in the options. +** This function instructs the resource manager to free all tagged data +** on certain occasions (see gfxop_new_pic). +*/ + + +int +gfxop_update_box(gfx_state_t *state, rect_t box); +/* Propagates a box from the back buffer to the front (visible) buffer +** Parameters: (gfx_state_t *) state: The affected state +** (rect_t) box: The box to propagate to the front buffer +** Returns : (int) GFX_OK or GFX_FATAL +** This function instructs the resource manager to free all tagged data +** on certain occasions (see gfxop_new_pic). +** When called with dirty rectangle management enabled, it will automatically +** propagate all dirty rectangles as well, UNLESS dirty frame accounting has +** been disabled explicitly. +*/ + +int +gfxop_enable_dirty_frames(gfx_state_t *state); +/* Enables dirty frame accounting +** Parameters: (gfx_state_t *) state: The state dirty frame accounting is to be enabled in +** Returns : (int) GFX_OK or GFX_ERROR if state was invalid +** Dirty frame accounting is enabled by default. +*/ + +int +gfxop_disable_dirty_frames(gfx_state_t *state); +/* Disables dirty frame accounting +** Parameters: (gfx_state_t *) state: The state dirty frame accounting is to be disabled in +** Returns : (int) GFX_OK or GFX_ERROR if state was invalid +*/ + + +/********************/ +/* Color operations */ +/********************/ + +int +gfxop_set_color(gfx_state_t *state, gfx_color_t *color, int r, int g, int b, int a, + int priority, int control); +/* Maps an r/g/b value to a color and sets a gfx_color_t structure +** Parameters: (gfx_state_t *) state: The current state +** (gfx_color_t *) color: Pointer to the structure to write to +** (int x int x int) r,g,b: The red/green/blue color intensity values +** of the result color (0x00 (minimum) to 0xff (max)) +** If any of these values is less than zero, the +** resulting color will not affect the visual map when +** used for drawing +** (int) a: The alpha (transparency) value, with 0x00 meaning absolutely +** opaque and 0xff meaning fully transparent. Alpha blending support +** is optional for drivers, so these are the only two values that +** are guaranteed to work as intended. Any value in between them +** must guarantee the following opaqueness: +** opaqueness(x-1) >= opaqueness(x) >= opaqueness (x+1) +** (i.e. ([0,255], less-transparent-than) must define a partial order) +** (int) priority: The priority to use for drawing, or -1 for none +** (int) control: The control to use for drawing, or -1 to disable drawing to the +** control map +** Returns : (int) GFX_OK or GFX_ERROR if state is invalid +** In palette mode, this may allocate a new color. Use gfxop_free_color() described below to +** free that color. +*/ + +int +gfxop_set_system_color(gfx_state_t *state, gfx_color_t *color); +/* Designates a color as a 'system color' +** Parameters: (gfx_state_t *) state: The affected state +** (gfx_color_t *) color: The color to designate as a system color +** Returns : (int) GFX_OK or GFX_ERROR if state is invalid +** System colors are permanent colors that cannot be deallocated. As such, they must be used +** with caution. +*/ + +int +gfxop_free_color(gfx_state_t *state, gfx_color_t *color); +/* Frees a color allocated by gfxop_set_color() +** Parmaeters: (gfx_state_t *) state: The state affected +** (gfx_color_t *) color: The color to de-allocate +** Returns : (int) GFX_OK or GFX_ERROR if state is invalid +** This function is a no-op in non-index mode, or if color is a system color. +*/ + + +/**********************/ +/* Pointer and IO ops */ +/**********************/ + +int +gfxop_usleep(gfx_state_t *state, long usecs); +/* Suspends program execution for the specified amount of microseconds +** Parameters: (gfx_state_t *) state: The state affected +** (long) usecs: The amount of microseconds to wait +** Returns : (int) GFX_OK or GFX_ERROR +** The mouse pointer will be redrawn continually, if applicable +*/ + +int +gfxop_set_pointer_cursor(gfx_state_t *state, int nr); +/* Sets the mouse pointer to a cursor resource +** Parameters: (gfx_state_t *) state: The affected state +** (int) nr: Number of the cursor resource to use +** Returns : (int) GFX_OK, GFX_ERROR if the resource did not +** exist and was not GFXOP_NO_POINTER, or GFX_FATAL on +** fatal error conditions. +** Use nr = GFX_NO_POINTER to disable the mouse pointer (default). +*/ + +int +gfxop_set_pointer_view(gfx_state_t *state, int nr, int loop, int cel, point_t *hotspot); +/* Sets the mouse pointer to a view resource +** Parameters: (gfx_state_t *) state: The affected state +** (int) nr: Number of the view resource to use +** (int) loop: View loop to use +** (int) cel: View cel to use +** (point_t *) hotspot: Manually set hotspot to use, or NULL for default. +** Returns : (int) GFX_OK or GFX_FATAL +** Use gfxop_set_pointer_cursor(state, GFXOP_NO_POINTER) to disable the +** pointer. +*/ + +int +gfxop_set_pointer_position(gfx_state_t *state, point_t pos); +/* Teleports the mouse pointer to a specific position +** Parameters: (gfx_state_t *) state: The state the pointer is in +** (point_t) pos: The position to teleport it to +** Returns : (int) Any error code or GFX_OK +** Depending on the graphics driver, this operation may be without +** any effect +*/ + +sci_event_t +gfxop_get_event(gfx_state_t *state, unsigned int mask); +/* Retreives the next input event from the driver +** Parameters: (gfx_state_t *) state: The affected state +** (int) mask: The event mask to poll from (see uinput.h) +** Returns : (sci_event_t) The next event in the driver's event queue, or +** a NONE event if no event matching the mask was found. +*/ + + +/*******************/ +/* View operations */ +/*******************/ + +int +gfxop_lookup_view_get_loops(gfx_state_t *state, int nr); +/* Determines the number of loops associated with a view +** Parameters: (gfx_state_t *) state: The state to use +** (int) nr: Number of the view to investigate +** Returns : (int) The number of loops, or GFX_ERROR if the view didn't exist +*/ + +int +gfxop_lookup_view_get_cels(gfx_state_t *state, int nr, int loop); +/* Determines the number of cels associated stored in a loop +** Parameters: (gfx_state_t *) state: The state to look up in +** (int) nr: Number of the view to look up in +** (int) loop: Number of the loop the number of cels of +** are to be investigated +** Returns : (int) The number of cels in that loop, or GFX_ERROR if either +** the view or the loop didn't exist +*/ + +int +gfxop_check_cel(gfx_state_t *state, int nr, int *loop, int *cel); +/* Clips the view/loop/cel position of a cel +** Parameters: (gfx_state_t *) state: The state to use +** (int) nr: Number of the view to use +** (int *) loop: Pointer to the variable storing the loop +** number to verify +** (int *) cel: Pointer to the variable storing the cel +** number to check +** Returns : (int) GFX_OK or GFX_ERROR if the view didn't exist +** *loop is clipped first, then *cel. The resulting setup will be a valid +** view configuration. +*/ + +int +gfxop_overflow_cel(gfx_state_t *state, int nr, int *loop, int *cel); +/* Resets loop/cel values to zero if they have become invalid +** Parameters: (gfx_state_t *) state: The state to use +** (int) nr: Number of the view to use +** (int *) loop: Pointer to the variable storing the loop +** number to verify +** (int *) cel: Pointer to the variable storing the cel +** number to check +** Returns : (int) GFX_OK or GFX_ERROR if the view didn't exist +** *loop is clipped first, then *cel. The resulting setup will be a valid +** view configuration. +*/ + +int +gfxop_get_cel_parameters(gfx_state_t *state, int nr, int loop, int cel, + int *width, int *height, point_t *offset); +/* Retreives the width and height of a cel +** Parameters: (gfx_state_t *) state: The state to use +** (int) nr: Number of the view +** (int) loop: Loop number to examine +** (int) cel: The cel (inside the loop) to look up +** (int *) width: The variable the width will be stored in +** (int *) height: The variable the height will be stored in +** (point_t *) offset: The variable the cel's x/y offset will be stored in +** Returns : (int) GFX_OK if the lookup succeeded, GFX_ERROR if the nr/loop/cel +** combination was invalid +*/ + +int +gfxop_draw_cel(gfx_state_t *state, int nr, int loop, int cel, point_t pos, + gfx_color_t color, int palette); +/* Draws (part of) a cel to the back buffer +** Parameters: (gfx_state_t *) state: The state encapsulating the driver to draw with +** (int) nr: Number of the view to draw +** (int) loop: Loop of the cel to draw +** (int) cel: The cel number of the cel to draw +** (point_t) pos: The positino the cel is to be drawn to +** (gfx_color_t color): The priority and control values to use for drawing +** (int) palette: The palette to use +** Returns : (int) GFX_OK or GFX_FATAL +*/ + + +int +gfxop_draw_cel_static(gfx_state_t *state, int nr, int loop, int cel, point_t pos, + gfx_color_t color, int palette); +/* Draws a cel to the static buffer; no clipping is performed +** Parameters: (gfx_state_t *) state: The state encapsulating the driver to draw with +** (int) nr: Number of the view to draw +** (int) loop: Loop of the cel to draw +** (int) cel: The cel number of the cel to draw +** (point_t) pos: The positino the cel is to be drawn to +** (gfx_color_t color): The priority and control values to use for drawing +** (int) palette: The palette to use +** Returns : (int) GFX_OK or GFX_FATAL +** Let me repeat, no clipping (except for the display borders) is performed. +*/ + + +int +gfxop_draw_cel_static_clipped(gfx_state_t *state, int nr, int loop, int cel, point_t pos, + gfx_color_t color, int palette); +/* Draws (part of) a clipped cel to the static buffer +** Parameters: (gfx_state_t *) state: The state encapsulating the driver to draw with +** (int) nr: Number of the view to draw +** (int) loop: Loop of the cel to draw +** (int) cel: The cel number of the cel to draw +** (point_t) pos: The positino the cel is to be drawn to +** (gfx_color_t color): The priority and control values to use for drawing +** (int) palette: The palette to use +** Returns : (int) GFX_OK or GFX_FATAL +** This function does clip. +*/ + + +/******************/ +/* Pic operations */ +/******************/ +/* These operations are exempt from clipping */ + +int +gfxop_new_pic(gfx_state_t *state, int nr, int flags, int default_palette); +/* Draws a pic and writes it over the static buffer +** Parameters: (gfx_state_t *) state: The state affected +** (int) nr: Number of the pic to draw +** (int) flags: Interpreter-dependant flags to use for drawing +** (int) default_palette: The default palette for drawing +** Returns : (int) GFX_OK or GFX_FATAL +** This function instructs the resource manager to tag all data as "unused". +** See the resource manager tag functions for a full description. +*/ + +void * +gfxop_get_pic_metainfo(gfx_state_t *state); +/* Retreives all meta-information assigned to the current pic +** Parameters: (gfx_state_t *) state: The state affected +** Returns : (void *) NULL if the pic doesn't exist or has no meta-information, +** the meta-info otherwise +** This meta-information is referred to as 'internal data' in the pic code +*/ + +int +gfxop_add_to_pic(gfx_state_t *state, int nr, int flags, int default_palette); +/* Adds a pic to the static buffer +** Parameters: (gfx_state_t *) state: The state affected +** (int) nr: Number of the pic to add +** (int) flags: Interpreter-dependant flags to use for drawing +** (int) default_palette: The default palette for drawing +** Returns : (int) GFX_OK or GFX_FATAL +*/ + + + + +/*******************/ +/* Text operations */ +/*******************/ + + +int +gfxop_get_font_height(gfx_state_t *state, int font_nr); +/* Returns the fixed line height for one specified font +** Parameters: (gfx_state_t *) state: The state to work on +** (int) font_nr: Number of the font to inspect +** Returns : (int) GFX_ERROR, GFX_FATAL, or the font line height +*/ + +int +gfxop_get_text_params(gfx_state_t *state, int font_nr, const char *text, + int maxwidth, int *width, int *height, int flags, + int *lines_nr, int *lineheight, int *lastline_width); +/* Calculates the width and height of a specified text in a specified font +** Parameters: (gfx_state_t *) state: The state to use +** (int) font_nr: Font number to use for the calculation +** (const char *) text: The text to examine +** (int) flags: ORred GFXR_FONT_FLAGs +** (int) maxwidth: The maximum pixel width to allow for the text +** Returns : (int) GFX_OK or GFX_ERROR if the font didn't exist +** (int) *width: The resulting width +** (int) *height: The resulting height +** (int) *lines_nr: Number of lines used in the text +** (int) *lineheight: Pixel height (SCI scale) of each text line +** (int) *lastline_wdith: Pixel offset (SCI scale) of the space +** after the last character in the last line +*/ + +gfx_text_handle_t * +gfxop_new_text(gfx_state_t *state, int font_nr, char *text, int maxwidth, + gfx_alignment_t halign, gfx_alignment_t valign, + gfx_color_t color1, gfx_color_t color2, gfx_color_t bg_color, + int flags); +/* Generates a new text handle that can be used to draw any text +** Parameters: (gfx_state_t *) state: The state to use +** (int) font_nr: Font number to use for the calculation +** (char *) text: The text to examine +** (int) maxwidth: The maximum pixel width to allow for the text +** (gfx_alignment_t) halign: The horizontal text alignment +** (gfx_alignment_t) valign: The vertical text alignment +** (gfx_color_t x gfx_color_t) color1, color2: The text's foreground colors +** (the function will dither between those two) +** (gfx_color_t) bg_color: The background color +** (int) flags: ORred GFXR_FONT_FLAGs +** Returns : (gfx_text_handle_t *) A newly allocated gfx_text_handle_t, or +** NULL if font_nr was invalid +** The control and priority values for the text will be extracted from color1. +** Note that the colors must have been allocated properly, or the text may display in +** incorrect colors. +*/ + +int +gfxop_free_text(gfx_state_t *state, gfx_text_handle_t *handle); +/* Frees a previously allocated text handle and all related resources +** Parameters: (gfx_state_t *) state: The state to use +** (gfx_text_handle_t *) handle: The handle to free +** Returns : (int) GFX_OK +*/ + +int +gfxop_draw_text(gfx_state_t *state, gfx_text_handle_t *handle, rect_t zone); +/* Draws text stored in a text handle +** Parameters: (gfx_state_t *) state: The target state +** (gfx_text_handle_t *) handle: The text handle to use for drawing +** (rect_t) zone: The rectangular box to draw to. In combination with +** halign and valign, this defines where the text is +** drawn to. +** Returns : (int) GFX_OK or GFX_FATAL +*/ + + +/****************************/ +/* Manual pixmap operations */ +/****************************/ + +gfx_pixmap_t * +gfxop_grab_pixmap(gfx_state_t *state, rect_t area); +/* Grabs a screen section from the back buffer and stores it in a pixmap +** Parameters: (gfx_state_t *) state: The affected state +** (rect_t) area: The area to grab +** Returns : (gfx_pixmap_t *) A result pixmap, or NULL on error +** Obviously, this only affects the visual map +*/ + +int +gfxop_draw_pixmap(gfx_state_t *state, gfx_pixmap_t *pxm, rect_t zone, point_t pos); +/* Draws part of a pixmap to the screen +** Parameters: (gfx_state_t *) state: The affected state +** (gfx_pixmap_t *) pxm: The pixmap to draw +** (rect_t) zone: The segment of the pixmap to draw +** (point_t) pos: The position the pixmap should be drawn to +** Returns : (int) GFX_OK or any error code +*/ + +int +gfxop_free_pixmap(gfx_state_t *state, gfx_pixmap_t *pxm); +/* Frees a pixmap returned by gfxop_grab_pixmap() +** Parameters: (gfx_state_t *) state: The affected state +** (gfx_pixmap_t *) pxm: The pixmap to free +** Returns : (int) GFX_OK, or GFX_ERROR if the state was invalid +*/ + +/******************************/ +/* Dirty rectangle operations */ +/******************************/ + +gfx_dirty_rect_t * +gfxdr_add_dirty(gfx_dirty_rect_t *base, rect_t box, int strategy); +/* Adds a dirty rectangle to 'base' according to a strategy +** Parameters: (gfx_dirty_rect_t *) base: The base rectangle to add to, or NULL +** (rect_t) box: The dirty frame to add +** (int) strategy: The dirty frame heuristic to use (see gfx_options.h) +** Returns : (gfx_dirty_rect_t *) an appropriate singly-linked dirty rectangle +** result cluster +*/ + +int +_gfxop_clip(rect_t *rect, rect_t clipzone); +/* Clips a rectangle against another one +** Parameters: (rect_t *) rect: The rectangle to clip +** (rect_t) clipzone: The outer bounds rect must be in +** Reuturns : (int) 1 if rect is empty now, 0 otherwise +*/ + +#endif /* !_GFX_OPERATIONS_H_ */ diff --git a/engines/sci/include/gfx_options.h b/engines/sci/include/gfx_options.h new file mode 100644 index 0000000000..51b31f794c --- /dev/null +++ b/engines/sci/include/gfx_options.h @@ -0,0 +1,86 @@ +/*************************************************************************** + gfx_options.h Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + + +#ifndef _GFX_OPTIONS_H_ +#define _GFX_OPTIONS_H_ + +#include +#include +#include + +/* Dirty rectangle heuristics: */ + +/* One: Redraw one rectangle surrounding the dirty area (insert is O(1)) */ +#define GFXOP_DIRTY_FRAMES_ONE 1 + +/* Clusters: Accumulate dirty rects, merging those that overlap (insert is O(n)) */ +#define GFXOP_DIRTY_FRAMES_CLUSTERS 2 + + +typedef struct _gfx_options { + /* gfx_options_t: Contains all user options to the rendering pipeline */ + /* See note in sci_conf.h for config_entry_t before changing types of + ** variables */ + + int buffer_pics_nr; /* Number of unused pics to buffer */ + + int correct_rendering; /* Whether to render slow, but correct (rather than + ** fast and almost correct) */ + + /* SCI0 pic resource options */ + int pic0_unscaled; /* Don't draw scaled SCI0 pics */ + + int pic0_dither_mode; /* Defined in gfx_resource.h */ + int pic0_dither_pattern; /* Defined in gfx_resource.h */ + + gfx_brush_mode_t pic0_brush_mode; + gfx_line_mode_t pic0_line_mode; + + gfx_xlate_filter_t cursor_xlate_filter; + gfx_xlate_filter_t view_xlate_filter; + gfx_xlate_filter_t pic_xlate_filter; /* Only relevant if (pic0_unscaled) */ + gfx_xlate_filter_t text_xlate_filter; + gfxr_font_scale_filter_t fixed_font_xlate_filter; /* Scale filter for systems that provide font support which isn't scaled */ + + gfxr_antialiasing_t pic0_antialiasing; + + gfx_res_fullconf_t res_conf; /* Resource customisation: Per-resource palettes etc. */ + + int dirty_frames; + + int workarounds; /* Workaround flags- see below */ + + rect_t pic_port_bounds; +} gfx_options_t; + +/* SQ3 counts whitespaces towards the total text size, as does gfxop_get_text_params() if this is set: */ +#define GFX_WORKAROUND_WHITESPACE_COUNT (1<<0) + + +#endif /* !_GFX_OPTIONS_H_ */ + diff --git a/engines/sci/include/gfx_res_options.h b/engines/sci/include/gfx_res_options.h new file mode 100644 index 0000000000..54138a8813 --- /dev/null +++ b/engines/sci/include/gfx_res_options.h @@ -0,0 +1,134 @@ +/*************************************************************************** + gfx_res_options.h Copyright (C) 2002 Christoph Reichenbach + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + + Please contact the maintainer for any program-related bug reports or + inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +/* Configuration options for per-resource customisations */ + +#ifndef _GFX_RES_OPTIONS_H_ +#define _GFX_RES_OPTIONS_H_ + +#include +#include + +#define GFX_RES_PATTERN_MIN 0 +#define GFX_RES_PATTERN_MAX 65535 + +typedef struct _gfx_res_pattern { + int min, max; +} gfx_res_pattern_t; + +typedef struct _gfx_res_pattern_list { + gfx_res_pattern_t pattern; + struct _gfx_res_pattern_list *next; +} gfx_res_pattern_list_t; + + +/* GFX resource assignments */ + +#define GFX_RES_ASSIGN_TYPE_PALETTE 0 /* Assign a palette */ + +typedef struct { + short type; /* GFX_RES_ASSIGN_TYPE_* */ + + union { + struct { + int colors_nr; + gfx_pixmap_color_t *colors; + } palette; + } assign; +} gfx_res_assign_t; + + +/* GFX resource modifications */ + +#define GFX_RES_MULTIPLY_FIXED 0 /* Linear palette update */ + +typedef struct { + short type; /* GFX_RES_ASSIGN_TYPE_* */ + + union { + byte factor[3]; /* divide by 16 to retreive factor */ + } mod; +} gfx_res_mod_t; + + +typedef struct _gfx_res_conf { + int type; /* Resource type-- only one allowed */ + + /* If any of the following is 0, it means that there is no restriction. + ** Otherwise, one of the patterns associated with them must match. */ + int patterns_nr; /* Number of patterns (only 'view' patterns for views) */ + int loops_nr, cels_nr; /* Number of loop/cel patterns, for views only. + ** For pics, loops_nr identifies the palette. */ + + gfx_res_pattern_t *patterns; + + union { + gfx_res_assign_t assign; + gfx_res_mod_t mod; + } conf; /* The actual configuration */ + + struct _gfx_res_conf *next; +} gfx_res_conf_t; + + +typedef gfx_res_conf_t *gfx_res_conf_p_t; + +typedef struct { + gfx_res_conf_p_t assign[GFX_RESOURCE_TYPES_NR]; + gfx_res_conf_p_t mod[GFX_RESOURCE_TYPES_NR]; +} gfx_res_fullconf_t; + + +struct _gfx_options; + +int +gfx_update_conf(struct _gfx_options *options, + char *line); +/* Updates the configuration +** Parameters: (gfx_options_t *) options: The options list to update +** (char *) line: The text line to parse +** Modifies : (gfx_options_t *) options +** Returns : (int) 0 on success, 1 if an error occured +** The line passed to it should begin with the resource type and be +** terminated by a semicolon. +*/ + +int +gfx_get_res_config(struct _gfx_options *options, + gfx_pixmap_t *pxm); +/* Configures a graphical pixmap according to config options +** Parameters: (gfx_options_t *) options: The options according to which +** configuration should be performed +** (gfx_resource_type_t) pxm: The pixmap to configure +** Returns : (int) 0 on success, non-zero otherwise +** Modifies pxm as considered appropriate by configuration options. Does +** not do anything in colour index mode. +*/ + +#endif /* !_GFX_RES_OPTIONS_H_ */ diff --git a/engines/sci/include/gfx_resmgr.h b/engines/sci/include/gfx_resmgr.h new file mode 100644 index 0000000000..f6a476b76f --- /dev/null +++ b/engines/sci/include/gfx_resmgr.h @@ -0,0 +1,354 @@ +/*************************************************************************** + gfx_resmgr.h Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef _GFX_RESMGR_H_ +#define _GFX_RESMGR_H_ + +#include +#include + +typedef enum { + GFX_RESOURCE_TYPE_VIEW = 0, + GFX_RESOURCE_TYPE_PIC, + GFX_RESOURCE_TYPE_FONT, + GFX_RESOURCE_TYPE_CURSOR, + GFX_RESOURCE_TYPE_PALETTE, + /* FIXME: Add PAL resource */ + + GFX_RESOURCE_TYPES_NR /* Number of resource types that are to be supported */ +} gfx_resource_type_t; + +#define GFX_RESOURCE_TYPE_0 GFX_RESOURCE_TYPE_VIEW + +#define GFXR_RES_ID(type, index) ((type) << 16 | (index)) +#define GFXR_RES_TYPE(id) (id >> 16) +#define GFXR_RES_NR(id) (id & 0xffff) + + +typedef struct gfx_resource_struct { + int ID; /* Resource ID */ + int lock_sequence_nr; /* See description of lock_counter in gfx_resstate_t */ + int mode; /* A mode type hash */ + + union { + gfx_pixmap_t *pointer; + gfxr_view_t *view; + gfx_bitmap_font_t *font; + gfxr_pic_t *pic; + } scaled_data; + + union { + gfx_pixmap_t *pointer; + gfxr_view_t *view; + gfx_bitmap_font_t *font; + gfxr_pic_t *pic; + } unscaled_data; + +} gfx_resource_t; + + +struct _gfx_options; + +typedef struct { + int version; /* Interpreter version */ + struct _gfx_options *options; + gfx_driver_t *driver; + gfx_pixmap_color_t *static_palette; + int static_palette_entries; + int lock_counter; /* Global lock counter; increased for each new resource allocated. + ** The newly allocated resource will then be assigned the new value + ** of the lock_counter, as will any resources referenced afterwards. + */ + int tag_lock_counter; /* lock counter value at tag time */ + + sbtree_t *resource_trees[GFX_RESOURCE_TYPES_NR]; + void *misc_payload; +} gfx_resstate_t; + + + +gfx_resstate_t * +gfxr_new_resource_manager(int version, struct _gfx_options *options, + gfx_driver_t *driver, void *misc_payload); +/* Allocates and initializes a new resource manager +** Parameters: (int) version: Interpreter version +** (gfx_options_t *): Pointer to all relevant drawing options +** (gfx_driver_t *): The graphics driver (needed for capability flags and the mode +** structure) +** (void *) misc_payload: Additional information for the interpreter's +** resource loaders +** Returns : (gfx_resstate_t *): A newly allocated resource manager +** The options are considered to be read-only, as they belong to the overlying state object. +*/ + +void +gfxr_free_resource_manager(gfx_driver_t *driver, gfx_resstate_t *state); +/* Frees a previously allocated resource manager, and all allocated resources. +** Parameters: (gfx_driver_t *) driver: The graphics driver; used to free pixmaps that +** are installed in a driver-specific registry +** (gfx_resstate_t *) state: The state manager to free +** Return : (void) +*/ + +void +gfxr_free_all_resources(gfx_driver_t *driver, gfx_resstate_t *state); +/* Frees all resources currently allocated +** Parameter: (gfx_driver_t *) driver: The driver to free with +** (gfx_resstate_t *) state: The state to do this on +** Returns : (void) +** This function is intended to be used primarily for debugging. +*/ + +void +gfxr_tag_resources(gfx_resstate_t *state); +/* 'Tags' all resources for deletion +** Paramters: (gfx_resstate_t *) state: The resource state to modify +** Returns : (void) +** Tagged resources are untagged if they are referenced. +*/ + +void +gfxr_free_tagged_resources(gfx_driver_t *driver, gfx_resstate_t *state); +/* Frees all tagged resources. +** Parameters: (gfx_driver_t *) driver: The graphics driver the pixmaps are potentially +** registered in +** (gfx_resstate_t *) state: The state to alter +** Returns : (void) +** Resources are tagged by calling gfx_tag_resources(), and untagged by calling the +** approprate dereferenciation function. +** Note that this function currently only affects view resources, as pic resources are +** treated differently, while font and cursor resources are relatively rare. +*/ + + +gfxr_pic_t * +gfxr_get_pic(gfx_resstate_t *state, int nr, int maps, int flags, + int default_palette, int scaled); +/* Retreives a displayable (translated) pic resource +** Parameters: (gfx_resstate_t *) state: The resource state +** (int) nr: Number of the pic resource +** (int) maps: The maps to translate (ORred GFX_MASK_*) +** (int) flags: Interpreter-dependant pic flags +** (int) default_palette: The default palette to use for drawing (if applicable) +** (int) scaled: Whether to return the scaled maps, or the unscaled +** ones (which may be identical) for some special operations. +** Returns : (gfx_pic_t *) The appropriate pic resource with all maps as index (but not +** neccessarily translated) data. +*/ + +gfxr_pic_t * +gfxr_add_to_pic(gfx_resstate_t *state, int old_nr, int new_nr, int maps, int flags, + int old_default_palette, int default_palette, int scaled); +/* Retreives a displayable (translated) pic resource written ontop of an existing pic +** Parameters: (gfx_resstate_t *) state: The resource state +** (int) old_nr: Number of the pic resource to write on +** (int) new_nr: Number of the pic resource that is to be added +** (int) maps: The maps to translate (ORred GFX_MASK_*) +** (int) flags: Interpreter-dependant pic flags +** (int) default_palette: The default palette to use for drawing (if applicable) +** (int) scaled: Whether to return the scaled maps, or the unscaled +** ones (which may be identical) for some special operations. +** Returns : (gfx_pic_t *) The appropriate pic resource with all maps as index (but not +** neccessarily translated) data. +** This function invalidates the cached pic pointed to by old_nr in the cache. While subsequent +** gfxr_add_to_pic() writes will still modify the 'invalidated' pic, gfxr_get_pic() operations will +** cause it to be removed from the cache and to be replaced by a clean version. +*/ + +gfxr_view_t * +gfxr_get_view(gfx_resstate_t *state, int nr, int *loop, int *cel, int palette); +/* Retreives a translated view cel +** Parameters: (gfx_resstate_t *) state: The resource state +** (int) nr: The view number +** (int *) loop: Pointer to a variable containing the loop number +** (int *) cel: Pointer to a variable containing the cel number +** (int) palette: The palette to use +** Returns : (gfx_view_t *) The relevant view, or NULL if nr was invalid +** loop and cel are given as pointers in order to allow the underlying variables to be +** modified if they are invalid (this is relevant for SCI version 0, where invalid +** loop and cel numbers have to be interpreted as 'maximum' or 'minimum' by the interpreter) +*/ + +gfx_bitmap_font_t * +gfxr_get_font(gfx_resstate_t *state, int nr, int scaled); +/* Retreives a font +** Parameters: (gfx_resstate_t *) state: The relevant resource state +** (int) nr: The font number +** (int) scaled: Whether the font should be font-scaled +** Returns : (gfx_font_t *) The appropriate font, or NULL on error +*/ + +gfx_pixmap_t * +gfxr_get_cursor(gfx_resstate_t *state, int nr); +/* Retreives an SCI0/SCI01 mouse cursor +** Parameters: (gfx_resstate_t *) state: The resource state +** (int) nr: The cursour number +** Returns : (gfx_font_t *) The approprate cursor as a pixmap, or NULL on error +*/ + +gfx_pixmap_color_t * +gfxr_get_palette(gfx_resstate_t *state, int nr); +/* Retreives a palette +** Parameters: (gfx_resstate_t *) state: The resource state +** (int) nr: The cursour number +** Returns : (gfx_font_t *) The approprate cursor as a pixmap, or NULL on error +*/ + + +/* =========================== */ +/* Interpreter-dependant stuff */ +/* =========================== */ + + +int +gfxr_interpreter_options_hash(gfx_resource_type_t type, int version, + struct _gfx_options *options, void *internal, int palette); +/* Calculates a unique hash value for the specified options/type setup +** Parameters: (gfx_resource_type_t) type: The type the hash is to be generated for +** (int) version: The interpreter type and version +** (gfx_options_t *) options: The options to hashify +** (void *) internal: Internal information provided by the interpreter +** (int) palette: The palette to use (FIXME: should this be here?) +** Returns : (int) A hash over the values of the options entries, covering entries iff +** they are relevant for the specified type +** Covering more entries than relevant may slow down the system when options are changed, +** while covering less may result in invalid cached data being used. +** Only positive values may be returned, as negative values are used internally by the generic +** resource manager code. +** Also, only the lower 20 bits are available to the interpreter. +** (Yes, this isn't really a "hash" in the traditional sense...) +*/ + +int * +gfxr_interpreter_get_resources(gfx_resstate_t *state, gfx_resource_type_t type, + int version, int *entries_nr, void *internal); +/* Retreives all resources of a specified type that are available from the interpreter +** Parameters: (gfx_resstate_t *) state: The relevant resource state +** (gfx_respirce_type_t) type: The resource type to query +** (int) version: The interpreter type and version +** (int *) entries_nr: The variable the number of entries will eventually be stored in +** (void *) internal: Internal information provided by the interpreter +** Returns : (int *) An array of resource numbers +** Unsupported/non-existing resources should return NULL here; this is equivalent to supported +** resources of which zero are available. +** The returned structure (if non-zero) must be freed by the querying code (the resource manager). +*/ + +gfxr_pic_t * +gfxr_interpreter_init_pic(int version, gfx_mode_t *mode, int ID, void *internal); +/* Initializes a pic +** Parameters: (int) version: Interpreter version to use +** (gfx_mode_t *) mode: The graphics mode the pic will be using +** (int) ID: The ID to assign to the gfxr_pic_t structure +** (void *) internal: Internal information provided by the interpreter +** Returns : (gfxr_pic_t *) A newly allocated pic +** This function is typically called befode gfxr_interpreter_clear_pic(). +** Must remember to initialize 'internal' to NULL or a malloc()'d area. +*/ + +void +gfxr_interpreter_clear_pic(int version, gfxr_pic_t *pic, void *internal); +/* Clears a previously allocated pic +** Parameters: (int) version: Interpreter version +** (gfxr_pic_t *) pic: The pic to clear +** (void *) internal: Internal information provided by the interpreter +** Returns : (void) +** This function is called in preparation for the pic to be drawn with gfxr_interpreter_calculate_pic. +*/ + +int +gfxr_interpreter_calculate_pic(gfx_resstate_t *state, gfxr_pic_t *scaled_pic, gfxr_pic_t *unscaled_pic, + int flags, int default_palette, int nr, void *internal); +/* Instructs the interpreter-specific code to calculate a picture +** Parameters: (gfx_resstate_t *) state: The resource state, containing options and version information +** (gfxr_pic_t *) scaled_pic: The pic structure that is to be written to +** (gfxr_pic_t *) unscaled_pic: The pic structure the unscaled pic is to be written to, +** or NULL if it isn't needed. +** (int) flags: Pic drawing flags (interpreter dependant) +** (int) default_palette: The default palette to use for pic drawing (interpreter dependant) +** (int) nr: pic resource number +** (void *) internal: Internal information provided by the interpreter +** Returns : (int) GFX_ERROR if the resource could not be found, GFX_OK otherwise +*/ + +gfxr_view_t * +gfxr_interpreter_get_view(gfx_resstate_t *state, int nr, void *internal, int palette); +/* Instructs the interpreter-specific code to calculate a view +** Parameters: (gfx_resstate_t *) state: The resource manager state +** (int) nr: The view resource number +** (void *) internal: Internal information provided by the interpreter +** Returns : (gfx_view_t *) The appropriate view, or NULL on error +*/ + +gfx_bitmap_font_t * +gfxr_interpreter_get_font(gfx_resstate_t *state, int nr, void *internal); +/* Instructs the interpreter-specific code to calculate a font +** Parameters: (gfx_resstate_t *) state: The resource manager state +** (int) nr: The font resource number +** (void *) internal: Internal information provided by the interpreter +** Returns : (gfx_font_t *) The newly calculated font, or NULL on error +*/ + +gfx_pixmap_t * +gfxr_interpreter_get_cursor(gfx_resstate_t *state, int nr, void *internal); +/* Instructs the interpreter-specific code to calculate a cursor +** Paramaters: (gfx_resstate_t *) state: The resource manager state +** (int nr): The cursor resource number +** (void *) internal: Internal information provided by the interpreter +** Returns : (gfx_pixmap_t *) The cursor pixmap, or NULL on error +*/ + +gfx_pixmap_color_t * +gfxr_interpreter_get_static_palette(gfx_resstate_t *state, int version, int *colors_nr, void *internal); +/* Retreives the static palette from the interpreter-specific code +** Parameters: (int) version: Interpreter version to use +** (int *) colors_nr: Number of colors to use +** (void *) internal: Internal information provided by the interpreter +** Returns : (gfx_pixmap_color_t *) *colors_nr static color entries +** if a static palette must be used, NULL otherwise +*/ + +gfx_pixmap_color_t * +gfxr_interpreter_get_palette(gfx_resstate_t *state, int version, int *colors_nr, void *internal, int nr); +/* Retreives the static palette from the interpreter-specific code +** Parameters: (int) version: Interpreter version to use +** (int *) colors_nr: Number of colors to use +** (void *) internal: Internal information provided by the interpreter +** Returns : (gfx_pixmap_color_t *) *colors_nr static color entries +** if a static palette must be used, NULL otherwise +*/ + +int +gfxr_interpreter_needs_multicolored_pointers(int version, void *internal); +/* Determines whether support for pointers with more than two colors is required +** Parameters: (int) version: Interpreter version to test for +** (void *) internal: Internal information provided by the interpreter +** Returns : (int) 0 if no support for multi-colored pointers is required, non-0 +** otherwise +*/ + +#endif /* !_GFX_RSMGR_H_ */ diff --git a/engines/sci/include/gfx_resource.h b/engines/sci/include/gfx_resource.h new file mode 100644 index 0000000000..34160c5d5d --- /dev/null +++ b/engines/sci/include/gfx_resource.h @@ -0,0 +1,440 @@ +/*************************************************************************** + gfx_resource.h Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +/* SCI Resource library */ + +#ifndef _GFX_RESOURCE_H_ +#define _GFX_RESOURCE_H_ + +#include +#include + +/*** Styles for pic0 drawing ***/ +/* Dithering modes */ +#define GFXR_DITHER_MODE_D16 0 /* Sierra SCI style */ +#define GFXR_DITHER_MODE_F256 1 /* Flat color interpolation */ +#define GFXR_DITHER_MODE_D256 2 /* 256 color dithering */ +/* Dithering patterns */ +#define GFXR_DITHER_PATTERN_SCALED 0 /* Dither per pixel on the 320x200 grid */ +#define GFXR_DITHER_PATTERN_1 1 /* Dither per pixel on the target */ + +#define SCI_TITLEBAR_SIZE 10 + +#define DRAWPIC01_FLAG_FILL_NORMALLY 1 +#define DRAWPIC01_FLAG_OVERLAID_PIC 2 + +#define GFXR_AUX_MAP_SIZE (320*200) + + +#define GFX_SCI0_IMAGE_COLORS_NR 16 +#define GFX_SCI0_PIC_COLORS_NR 256 + +#define GFX_SCI1_AMIGA_COLORS_NR 32 + +extern int sci0_palette; + +/* (gfx_pic_0.c) The 16 EGA base colors */ +extern gfx_pixmap_color_t gfx_sci0_image_colors[][16]; + +/* (gfx_pic_0.c) The 256 interpolated colors (initialized when +** gfxr_init_pic() is called for the first time, or when gfxr_init_static_palette() is called) +*/ +extern gfx_pixmap_color_t gfx_sci0_pic_colors[]; + + +typedef struct { + gfx_line_mode_t line_mode; /* one of GFX_LINE_MODE_* */ + gfx_brush_mode_t brush_mode; + rect_t pic_port_bounds; +} gfxr_pic0_params_t; + +typedef struct { + int ID; /* pic number (NOT resource ID, just number) */ + gfx_mode_t *mode; + gfx_pixmap_t *visual_map; + gfx_pixmap_t *priority_map; + gfx_pixmap_t *control_map; + + byte aux_map[GFXR_AUX_MAP_SIZE]; + +/* Auxiliary map details: +** Bit 0: Vis +** Bit 1: Pri +** Bit 2: Ctrl +** Bit 3-5: 'filled' (all three bits are set to 1) +*/ + + rect_t bounds; + + void *undithered_buffer; /* copies visual_map->index_data before dithering */ + int undithered_buffer_size; + + void *internal; /* Interpreter information, or NULL. Will be freed + ** automatically when the pic is freed! */ + +} gfxr_pic_t; + + +typedef struct { + int cels_nr; + gfx_pixmap_t **cels; +} gfxr_loop_t; + + +typedef struct { + int ID; + + int flags; + int colors_nr; + gfx_pixmap_color_t *colors; + + int loops_nr; + gfxr_loop_t *loops; + + int translation[GFX_SCI0_IMAGE_COLORS_NR]; +} gfxr_view_t; + + +typedef enum { + GFXR_FONT_SCALE_FILTER_NONE +} gfxr_font_scale_filter_t; + + +typedef struct { + const char *offset; + int length; +} text_fragment_t; + +/* unscaled color index mode: Used in addition to a scaled mode +** to render the pic resource twice. See gfxr_remove_artifacts_pic0(). +*/ +extern gfx_mode_t mode_1x1_color_index; + +void +gfxr_init_static_palette(void); +/* Initializes the static 256 color palette +** Parameters: (void) +** Returns : (void) +*/ + +gfxr_pic_t * +gfxr_init_pic(gfx_mode_t *mode, int ID, int sci1); +/* Initializes a gfxr_pic_t for a specific mode +** Parameters: (gfx_mode_t *) mode: The specific graphics mode +** (int) ID: The ID to assign to the resulting pixmaps +** Returns : (gfxr_pic_t *) The allocated pic resource, or NULL on error. +** This function allocates memory for use by resource drawer functions. +*/ + +void +gfxr_free_pic(gfx_driver_t *driver, gfxr_pic_t *pic); +/* Uninitializes a pic resource +** Parameters: (gfx_driver_t *) driver: The driver the pic should be removed from +** (gfxr_pic_t *) pic: The pic to free +** Returns : (void) +*/ + +void +gfxr_free_view(gfx_driver_t *driver, gfxr_view_t *view); +/* Frees all memory associated with a view +** Paremeters: (gfx_driver_t *) driver: The driver the view should be removed from +** (gfxr_view_t *) view: The view to free +** Returns : (void) +*/ + + +/*******************/ +/* Font operations */ +/*******************/ +/* SCI0, SCI01 and SCI1 all use the same font format. */ + +/* SQ3 uses a somewhat different scheme for calculating text sizes: it counts +** whitespace while calculating the text size. */ +#define GFXR_FONT_FLAG_COUNT_WHITESPACE (1<<0) +/* Don't give newline characters special semantics */ +#define GFXR_FONT_FLAG_NO_NEWLINES (1<<1) +/* Interpret CR LF sequences as a single newline, rather than two of them */ +#define GFXR_FONT_FLAG_EAT_TRAILING_LF (1<<2) + + +gfx_bitmap_font_t * +gfxr_read_font(int id, byte *resource, int size); +/* Geneartes a bitmap font data structure from a resource +** Parameters: (int) id: Resource ID of the resulting font +** (byte *) resource: Pointer to the resource data +** (int) size: Size of the resource block +** Returns : (gfx_bitmap_font_t *) The resulting font structure, or +** NULL on error +*/ + +void +gfxr_free_font(gfx_bitmap_font_t *font); +/* Frees a previously allocated font structure +** Parameters: (gfx_bitmap_font_t *) font: The font to free +** Returns : (void) +*/ + +gfx_bitmap_font_t * +gfxr_scale_font(gfx_bitmap_font_t *font, gfx_mode_t *mode, gfxr_font_scale_filter_t filter); +/* Scales a font resource +** Parameters: (gfx_bitmap_font_t *) font: The font to scale +** (gfx_mode_t *) mode: The graphics mode to scale it for +** (gfxr_font_scale_filter_t) filter: A filter to use +** Returns : (gfx_bitmap_font_t *) A scaled font, or NULL on error +*/ + +text_fragment_t * +gfxr_font_calculate_size(gfx_bitmap_font_t *font, int max_width, const char *text, + int *width, int *height, + int *lines, int *line_height, int *last_offset, + int flags); +/* Calculates the size that would be occupied by drawing a specified text +** Parameters: (gfx_bitmap_font_t *) font: The font to calculate with +** (int) max_width: Maximum pixel width allowed for the output +** (const char *) text: The text to calculate for +** (int) flags: Any text formatting flags +** Returns : (text_fragment *) a newly allocated array of text_fragments, +** containing the start and size of each string +** segment +** (int) *width: The resulting width +** (int) *height: The resulting height +** (int) *lines: Number of lines used +** (int) *line_height: Pixel height of a single line of text +** (int) *last_offset: Pixel offset after the last drawn line +** This function assumes 320x200 mode. +*/ + +gfx_pixmap_t * +gfxr_draw_font(gfx_bitmap_font_t *font, const char *text, int characters, + gfx_pixmap_color_t *fg0, gfx_pixmap_color_t *fg1, gfx_pixmap_color_t *bg); +/* Draws text in a specific font to a pixmap +** Parameters: (gfx_bitmap_font_t *) font: The font to use for drawing +** (char *) text: The start of the text to draw +** (int) characters: The number of characters to draw +** (gfx_pixmap_color_t *) fg0: The first foreground color +** (gfx_pixmap_color_t *) fg1: The second foreground color +** (gfx_pixmap_color_t *) bg: The background color +** Returns : (gfx_pixmap_t *) The result pixmap, or NULL on error +** The results are written to the pixmap's index buffer. Contents of the +** foreground and background fields are copied into a newly allocated font +** structure, so that the pixmap may be translated directly. +** If any of the colors is null, it will be assumed to be transparent. +** In color index mode, the specified colors have to be preallocated. +*/ + + +/*********************/ +/* SCI0 operations */ +/*********************/ + + +void +gfxr_clear_pic0(gfxr_pic_t *pic, int sci_titlebar_size); +/* Clears all pic buffers of one pic +** Parameters: (gfxr_pic_t) pic: The picture to clear +** (int) sci_titlebar_size: How much space to reserve for the title bar +** Returns : (void) +** This function should be called before gfxr_draw_pic0, unless cumulative +** drawing is intended +*/ + + +void +gfxr_draw_pic01(gfxr_pic_t *pic, int fill_normally, int default_palette, + int size, byte *resource, gfxr_pic0_params_t *style, int resid, int sci1, + gfx_pixmap_color_t *static_pal, int static_pal_nr); +/* Draws a pic resource (all formats prior to SCI1.1) +** Parameters: (gfxr_pic_t *) pic: The pic to draw to +** (int) fill_normally: If 1, the pic is drawn normally; if 0, all +** fill operations will fill with black +** (int) default_palette: The default palette to use for drawing +** (int) size: Resource size +** (byte *) resource: Pointer to the resource data +** (gfxr_pic0_params_t *) style: The drawing style +** (int) resid: The resource ID +** (int) sci1: Nonzero if SCI1 +** (gfx_pixmap_color_t *) static_pal: The static palette +** (int) static_pal_nr: Number of entries in static palette +** Returns : (void) +** The result is stored in gfxr_visual_map, gfxr_priority_map, and gfxr_control_map. +** The palette entry of gfxr_visual_map is never used. +** Note that the picture will not be drawn dithered; use gfxr_dither_pic0 for that. +*/ + +void +gfxr_draw_pic11(gfxr_pic_t *pic, int fill_normally, int default_palette, + int size, byte *resource, gfxr_pic0_params_t *style, int resid, + gfx_pixmap_color_t *static_pal, int static_pal_nr); +/* Draws a pic resource (SCI1.1) +** Parameters: (gfxr_pic_t *) pic: The pic to draw to +** (int) fill_normally: If 1, the pic is drawn normally; if 0, all +** fill operations will fill with black +** (int) default_palette: The default palette to use for drawing +** (int) size: Resource size +** (byte *) resource: Pointer to the resource data +** (gfxr_pic0_params_t *) style: The drawing style +** (int) resid: The resource ID +** (gfx_pixmap_color_t *) static_pal: The static palette +** (int) static_pal_nr: Number of entries in static palette +** Returns : (void) +** The result is stored in gfxr_visual_map, gfxr_priority_map, and gfxr_control_map. +** The palette entry of gfxr_visual_map is never used. +** Note that the picture will not be drawn dithered; use gfxr_dither_pic0 for that. +*/ + +void +gfxr_remove_artifacts_pic0(gfxr_pic_t *dest, gfxr_pic_t *src); +/* Removes artifacts from a scaled pic +** Parameters: (gfxr_pic_t *) dest: The scaled pic +** (gfxr_pic_t *) src: An unscaled pic +** Returns : (void) +** Using information from the (correctly rendered) src pic, this function implements +** some heuristics to remove artifacts from dest. Must be used before dither_pic0 is +** called, because it operates on the index buffer. +*/ + +void +gfxr_dither_pic0(gfxr_pic_t *pic, int mode, int pattern); +/* Dithers a gfxr_visual_map +** Parameters: (gfxr_pic_t *) pic: The pic to dither +** (int) mode: One of GFXR_DITHER_MODE +** (int) pattern: One of GFXR_DITHER_PATTERN +** Returns : (void) +*/ + +gfxr_view_t * +gfxr_draw_view0(int id, byte *resource, int size, int palette); +/* Calculates an SCI0 view +** Parameters: (int) id: Resource ID of the view +** (byte *) resource: Pointer to the resource to read +** (int) size: Size of the resource +** (int) palette: The palette to use +** Returns : (gfxr_view_t *) The resulting view +*/ + +gfx_pixmap_t * +gfxr_draw_cursor0(int id, byte *resource, int size); +/* Calculates an SCI0 cursor +** Parameters: (int) id: The cursor's resource ID +** (byte *) resource: Pointer to the resource data +** (int) size: Resource size +** Returns : (gfx_pixmap_t *) A newly allocated pixmap storing the cursor +*/ + +/**********************/ +/* SCI01 operations */ +/**********************/ + +gfx_pixmap_t * +gfxr_draw_cursor01(int id, byte *resource, int size); +/* Calculates an SCI01 cursor +** Parameters: (int) id: The cursor's resource ID +** (byte *) resource: Pointer to the resource data +** (int) size: Resource size +** Returns : (gfx_pixmap_t *) A newly allocated pixmap containing an index +** color representation of the cursor +*/ + + +/*********************/ +/* SCI1 operations */ +/*********************/ + +gfx_pixmap_color_t * +gfxr_read_pal1(int id, int *colors_nr, byte *resource, int size); +/* Reads an SCI1 palette +** Parameters: (int) id: Resource ID for the palette (or the view it was found in) +** (int *) colors_nr: Pointer to the variable the number of colors +** will be stored in +** (byte *) resource: Source data +** (int) size: Size of the memory block pointed to by resource +** Returns : (gfx_pixmap_color_t *) *colors_nr color_t entries with the colors +*/ + +gfx_pixmap_color_t * +gfxr_read_pal1_amiga(int *colors_nr, FILE *f); +/* Reads an SCI1 palette +** Parameters: (int *) colors_nr: Pointer to the variable the number of colors +** will be stored in +** (FILE *) f: Palette file +** Returns : (gfx_pixmap_color_t *) *colors_nr color_t entries with the colors +*/ + +gfx_pixmap_color_t * +gfxr_read_pal11(int id, int *colors_nr, byte *resource, int size); +/* Reads an SCI1.1 palette +** Parameters: (int) id: Resource ID for the palette (or the view it was found in) +** (int *) colors_nr: Pointer to the variable the number of colors +** will be stored in +** (byte *) resource: Source data +** (int) size: Size of the memory block pointed to by resource +** Returns : (gfx_pixmap_color_t *) *colors_nr color_t entries with the colors +*/ + +gfxr_view_t * +gfxr_draw_view1(int id, byte *resource, int size, gfx_pixmap_color_t *static_pal, + int static_pal_nr); +/* Calculates an SCI1 view +** Parameters: (int) id: Resource ID of the view +** (byte *) resource: Pointer to the resource to read +** (int) size: Size of the resource +** (gfx_pixmap_color_t *) static_pal: The static palette +** (int) static_pal_nr: Number of entries in static palette +** Returns : (gfxr_view_t *) The resulting view +*/ + +gfx_pixmap_t * +gfxr_draw_cel11(int id, int loop, int cel, int mirrored, byte *resource_base, byte *cel_base, int size, gfxr_view_t *view); + + +gfx_pixmap_t * +gfxr_endianness_adjust(gfx_pixmap_t *pixmap, gfx_mode_t *mode); +/* Endianness-adjusts a pixmap, if neccessary +** Parameters: (gfx_pixmap_t *) pixmap: The pixmap to adjust +** (gfx_mode_t *) mode: The mode to adjust it for +** Returns : (gfx_pixmap_t *) pixmap, or NULL on error +** The pixmap is adjusted iff the mode signals that this is +** required (by means of setting the appropriate flag), and +** the mode has a byte depth of more than 1. +*/ + + +static inline int +get_uint_16(byte *offset) +{ + return ((unsigned int) offset[0] | (((unsigned int) offset[1]) << 8)); +} + +static inline int +get_int_16(byte *offset) +{ + return ((int) offset[0] | (((int) offset[1]) << 8)); +} + + +#endif /* !_GFX_RESOURCE_H_ */ + diff --git a/engines/sci/include/gfx_state_internal.h b/engines/sci/include/gfx_state_internal.h new file mode 100644 index 0000000000..dc12e3c8b9 --- /dev/null +++ b/engines/sci/include/gfx_state_internal.h @@ -0,0 +1,238 @@ +/*************************************************************************** + gfx_state_internal.h Copyright (C) 2000,01 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef _GFX_STATE_INTERNAL_H_ +#define _GFX_STATE_INTERNAL_H_ + +#include +#include +#include +#include +#include + + +#define GFXW_FLAG_VISIBLE (1<<0) +#define GFXW_FLAG_OPAQUE (1<<1) +#define GFXW_FLAG_CONTAINER (1<<2) +#define GFXW_FLAG_DIRTY (1<<3) +#define GFXW_FLAG_TAGGED (1<<4) +#define GFXW_FLAG_MULTI_ID (1<<5) /* Means that the ID used herein may be used more than once, i.e. is not unique */ +#define GFXW_FLAG_IMMUNE_TO_SNAPSHOTS (1<<6) /* Snapshot restoring doesn't kill this widget, and +5 bonus to saving throws vs. Death Magic */ +#define GFXW_FLAG_NO_IMPLICIT_SWITCH (1<<7) /* Ports: Don't implicitly switch to this port when disposing windows */ + +typedef struct { + int serial; /* The first serial number to kill */ + rect_t area; +} gfxw_snapshot_t; + +typedef enum { + GFXW_, /* Base widget */ + + GFXW_BOX, + GFXW_RECT, + GFXW_LINE, /* For lines, the bounding rectangle's xl, yl determine the line's expansion: + ** (x2, y2) = (x+xl, y+yl) */ + GFXW_INVERSE_LINE, + GFXW_VIEW, + GFXW_STATIC_VIEW, + GFXW_DYN_VIEW, + GFXW_PIC_VIEW, + GFXW_TEXT, + + GFXW_CONTAINER, + + GFXW_LIST, + GFXW_SORTED_LIST, + GFXW_VISUAL, + GFXW_PORT + +} gfxw_widget_type_t; + + +#define GFXW_MAGIC_VALID 0xC001 +#define GFXW_MAGIC_INVALID 0xbad + +#define GFXW_NO_ID -1 + +struct _gfxw_widget; +struct _gfxw_container_widget; +struct _gfxw_visual; + +typedef int gfxw_point_op(struct _gfxw_widget *, point_t); +typedef int gfxw_visual_op(struct _gfxw_widget *, struct _gfxw_visual *); +typedef int gfxw_op(struct _gfxw_widget *); +typedef int gfxw_op_int(struct _gfxw_widget *, int); +typedef int gfxw_bin_op(struct _gfxw_widget *, struct _gfxw_widget *); + +#define WIDGET_COMMON \ + int magic; /* Extra check after typecasting */ \ + int serial; /* Serial number */ \ + int flags; /* Widget flags */ \ + gfxw_widget_type_t type; \ + rect_t bounds; /* Boundaries */ \ + struct _gfxw_widget *next; /* Next widget in widget list */ \ + int ID; /* Unique ID or GFXW_NO_ID */ \ + int subID; /* A 'sub-ID', or GFXW_NO_ID */ \ + struct _gfxw_container_widget *parent; /* The parent widget, or NULL if not owned */ \ + struct _gfxw_visual *visual; /* The owner visual */ \ + int widget_priority; /* Drawing priority, or -1 */ \ + gfxw_point_op *draw; /* Draw widget (if dirty) and anything else required for the display to be consistant */ \ + gfxw_op *widfree; /* Remove widget (and any sub-widgets it may contain) */ \ + gfxw_op *tag; /* Tag the specified widget */ \ + gfxw_op_int *print; /* Prints the widget's contents, using sciprintf. Second parameter is indentation. */ \ + gfxw_bin_op *compare_to; /* a.compare_to(a, b) returns <0 if a0 if a>b */ \ + gfxw_bin_op *equals; /* a equals b if both cause the same data to be displayed */ \ + gfxw_bin_op *should_replace; /* (only if a equals b) Whether b should replace a even though they are equivalent */ \ + gfxw_bin_op *superarea_of; /* a superarea_of b <=> for each pixel of b there exists an opaque pixel in a at the same location */ \ + gfxw_visual_op *set_visual /* Sets the visual the widget belongs to */ + +typedef struct _gfxw_widget { + WIDGET_COMMON; +} gfxw_widget_t; + + +#define GFXW_IS_BOX(widget) ((widget)->type == GFXW_BOX) +typedef struct { + WIDGET_COMMON; + gfx_color_t color1, color2; + gfx_box_shade_t shade_type; +} gfxw_box_t; + + +#define GFXW_IS_PRIMITIVE(widget) ((widget)->type == GFXW_RECT || (widget)->type == GFXW_LINE || (widget->type == GFXW_INVERSE_LINE)) +typedef struct { + WIDGET_COMMON; + gfx_color_t color; + gfx_line_mode_t line_mode; + gfx_line_style_t line_style; +} gfxw_primitive_t; + + + +#define VIEW_COMMON \ + WIDGET_COMMON; \ + point_t pos; /* Implies the value of 'bounds' in WIDGET_COMMON */ \ + gfx_color_t color; \ + int view, loop, cel; \ + int palette + +#define GFXW_IS_VIEW(widget) ((widget)->type == GFXW_VIEW || (widget)->type == GFXW_STATIC_VIEW \ + || (widget)->type == GFXW_DYN_VIEW || (widget)->type == GFXW_PIC_VIEW) +typedef struct { + VIEW_COMMON; +} gfxw_view_t; + +#define GFXW_IS_DYN_VIEW(widget) ((widget)->type == GFXW_DYN_VIEW || (widget)->type == GFXW_PIC_VIEW) +typedef struct { + VIEW_COMMON; + /* fixme: This code is specific to SCI */ + rect_t draw_bounds; /* The correct position to draw to */ + void *under_bitsp, *signalp; + int under_bits, signal; + int z; /* The z coordinate: Added to y, but used for sorting */ + int sequence; /* Sequence number: For sorting */ + int force_precedence; /* Precedence enforcement variable for sorting- defaults to 0 */ +} gfxw_dyn_view_t; + + + +#define GFXW_IS_TEXT(widget) ((widget)->type == GFXW_TEXT) +typedef struct { + WIDGET_COMMON; + int font_nr; + int lines_nr, lineheight, lastline_width; + char *text; + gfx_alignment_t halign, valign; + gfx_color_t color1, color2, bgcolor; + int text_flags; + int width, height; /* Real text width and height */ + gfx_text_handle_t *text_handle; +} gfxw_text_t; + + +/* Container widgets */ + +typedef int gfxw_unary_container_op(struct _gfxw_container_widget *); +typedef int gfxw_container_op(struct _gfxw_container_widget *, gfxw_widget_t *); +typedef int gfxw_rect_op(struct _gfxw_container_widget *, rect_t, int); + +#define WIDGET_CONTAINER \ + WIDGET_COMMON; \ + rect_t zone; /* The writeable zone (absolute) for contained objects */ \ + gfx_dirty_rect_t *dirty; /* List of dirty rectangles */ \ + gfxw_widget_t *contents; \ + gfxw_widget_t **nextpp; /* Pointer to the 'next' pointer in the last entry in contents */ \ + gfxw_unary_container_op *free_tagged; /* Free all tagged contained widgets */ \ + gfxw_unary_container_op *free_contents; /* Free all contained widgets */ \ + gfxw_rect_op *add_dirty_abs; /* Add an absolute dirty rectangle */ \ + gfxw_rect_op *add_dirty_rel; /* Add a relative dirty rectangle */ \ + gfxw_container_op *add /* Append widget to an appropriate position (for view and control lists) */ + + +typedef struct _gfxw_container_widget { + WIDGET_CONTAINER; +} gfxw_container_t; + + +#define GFXW_IS_CONTAINER(widget) ((widget)->type == GFXW_PORT || (widget)->type == GFXW_VISUAL || \ + (widget)->type == GFXW_SORTED_LIST || (widget)->type == GFXW_LIST) + +#define GFXW_IS_LIST(widget) ((widget)->type == GFXW_LIST || (widget)->type == GFXW_SORTED_LIST) +#define GFXW_IS_SORTED_LIST(widget) ((widget)->type == GFXW_SORTED_LIST) +typedef gfxw_container_t gfxw_list_t; + +#define GFXW_IS_VISUAL(widget) ((widget)->type == GFXW_VISUAL) +typedef struct _gfxw_visual { + WIDGET_CONTAINER; + struct _gfxw_port **port_refs; /* References to ports */ + int port_refs_nr; + int font_nr; /* Default font */ + gfx_state_t *gfx_state; +} gfxw_visual_t; + +#define GFXW_IS_PORT(widget) ((widget)->type == GFXW_PORT) +typedef struct _gfxw_port { + WIDGET_CONTAINER; + + gfxw_list_t *decorations; /* optional window decorations- drawn before the contents */ + gfxw_widget_t *port_bg; /* Port background widget or NULL */ + gfx_color_t color, bgcolor; + int chrono_port; + int font_nr; + point_t draw_pos; /* Drawing position */ + gfxw_snapshot_t *restore_snap; /* Snapshot to be restored automagically, + experimental feature used in the PQ3 interpreter */ + int port_flags; /* interpreter-dependant flags */ + const char *title_text; + byte gray_text; /* Whether text is 'grayed out' (dithered) */ +} gfxw_port_t; + +#undef WIDGET_COMMON +#undef WIDGET_CONTAINER + +#endif /* !_GFX_STATE_INTERNAL_H_ */ + diff --git a/engines/sci/include/gfx_system.h b/engines/sci/include/gfx_system.h new file mode 100644 index 0000000000..e83f39fb8e --- /dev/null +++ b/engines/sci/include/gfx_system.h @@ -0,0 +1,445 @@ +/*************************************************************************** + gfx_system.h Copyright (C) 2000,01 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef _SCI_GFX_SYSTEM_ +#define _SCI_GFX_SYSTEM_ + +#include +#include +#include +#include +#include + + +#define GFX_DEBUG + +/* General output macros */ +#ifdef __GNUC__ +# define GFXERROR gfxprintf("GFX Error: %s, %s() L%d:", __FILE__, __FUNCTION__, __LINE__); sciprintf +# define GFXWARN gfxprintf("GFX Warning: %s, %s() L%d:", __FILE__, __FUNCTION__, __LINE__); sciprintf +# ifdef GFX_DEBUG +# define GFXDEBUG gfxprintf("GFX-debug: %s, %s() L%d:", __FILE__, __FUNCTION__, __LINE__); sciprintf +# else /* !GFX_DEBUG */ +# define GFXDEBUG if (0) printf +# endif /* !GFX_DEBUG */ +#else /* !__GNUC__ */ +# define GFXERROR gfxprintf("GFX Error: %s, L%d:", __FILE__, __LINE__); sciprintf +# define GFXWARN gfxprintf("GFX Warning: %s, L%d:", __FILE__, __LINE__); sciprintf +# ifdef GFX_DEBUG +# define GFXDEBUG gfxprintf("GFX-debug: %s, L%d:", __FILE__, __LINE__); sciprintf +# else /* !GFX_DEBUG */ +# define GFXDEBUG if (0) printf +# endif /* !GFX_DEBUG */ +#endif /* !__GNUC__ */ + +/***********************/ +/*** Data structures ***/ +/***********************/ + +#define GFX_COLOR_SYSTEM -1 + + +typedef struct { /* gfx_palette_color_t: Palette color description */ + + int lockers; /* Number of pixmaps holding a lock on that color. + ** 0 means that the color is unused, -1 means that it is + ** "system allocated" and may not be freed. */ + byte r,g,b; /* Red, green, blue; intensity varies from 0 (min) to 255 (max) */ + +} gfx_palette_color_t; + + + +typedef struct { /* gfx_palette_t: Palette description for color index modes */ + + int max_colors_nr; /* Maximum number of allocated colors */ + gfx_palette_color_t *colors; /* Actual colors, malloc()d as a block */ +} gfx_palette_t; + + + +#define GFX_MODE_IS_UNSCALED(mode) (((mode)->xfact == 1) && ((mode)->yfact == 1)) + +/* Reverse-endian: Target display has non-native endianness +** (BE if local is LE or the other way 'round */ +#define GFX_MODE_FLAG_REVERSE_ENDIAN (1<<0) +/* Reverse Alpha: Alpha values 0 mean "transparent" if this is +** enabled */ +#define GFX_MODE_FLAG_REVERSE_ALPHA (1<<1) + +typedef struct { /* gfx_mode_t: Graphics mode description */ + + int xfact, yfact; /* Horizontal and vertical scaling factors */ + int bytespp; /* Bytes per pixel */ + + unsigned int flags; /* GFX_MODE_FLAG_* Flags- see above */ + + + gfx_palette_t *palette; /* Palette or NULL to indicate non-palette mode. + ** Palette (color-index) mode is only supported + ** for bytespp=1. */ + + /* Color masks */ + unsigned int red_mask, green_mask, blue_mask, alpha_mask; + short red_shift, green_shift, blue_shift, alpha_shift; + + /* Each of the mask/shift pairs describe where the corresponding color + ** values are stored for the described mode. Internally, color + ** calculations are done by using 32 bit values for r, g, b, a. After + ** the internal values have been calculated, they are shifted RIGHT + ** by the xxx_shift amount described above, then ANDed with the + ** corresponding color mask; finally, all three results are ORred to- + ** gether. The alpha values are used as appropriate; if alpha_mask is + ** zero, then images use a special alpha map. */ + +} gfx_mode_t; + + + +#define GFX_COLOR_INDEX_UNMAPPED -1 + +typedef struct { /* gfx_pixmap_color_t: Pixmap-specific color entries */ + int global_index; /* Global index color or GFX_COLOR_INDEX_UNMAPPED. */ + guint8 r, g, b; /* Real color */ +} gfx_pixmap_color_t; + + + +typedef struct { /* gfx_color_t: Full color */ + gfx_pixmap_color_t visual; + guint8 alpha; /* transparency = (1-opacity) */ + byte priority, control; + byte mask; /* see mask values below */ +} gfx_color_t; + + + +typedef struct { /* rect_t: Rectangle description */ + int x, y; + int xl, yl; /* width, height: (x,y,xl,yl)=(5,5,1,1) occupies 1 pixel */ +} rect_t; + + +/* Generates a rect_t from index data +** Parameters: (int x int) x,y: Upper left point of the rectangle +** (int x int) xl, yl: Horizontal and vertical extension of the rectangle +** Returns : (rect_t) A rectangle matching the supplied parameters +*/ +static inline rect_t +gfx_rect(int x, int y, int xl, int yl) +{ + rect_t rect; + + rect.x = x; + rect.y = y; + rect.xl = xl; + rect.yl = yl; + + return rect; +} + +#define GFX_PRINT_RECT(rect) (rect).x, (rect).y, (rect).xl, (rect).yl + +#define OVERLAP(a, b, z, zl) (a.z >= b.z && a.z < (b.z + b.zl)) + +/* Determines whether two rects overlap +** Parameters: (rect_t x rect_t) a,b: The two rect_ts to check for overlap +** Returns : (int) 1 if they overlap, 0 otherwise +*/ +static inline int +gfx_rects_overlap(rect_t a, rect_t b) +{ + return (OVERLAP(a, b, x, xl) || OVERLAP(b, a, x, xl)) + && (OVERLAP(a, b, y, yl) || OVERLAP(b, a, y, yl)); +} + +#undef OVERLAP + +#define MERGE_PARTIAL(z, zl) \ +if (a.z < b.z) SUBMERGE_PARTIAL(a, b, z, zl) \ +else SUBMERGE_PARTIAL(b, a, z, zl) + +#define SUBMERGE_PARTIAL(a, b, z, zl) \ +{ \ + retval.z = a.z; \ + retval.zl = a.zl; \ + if (b.z + b.zl > a.z + a.zl) \ + retval.zl = (b.z + b.zl - a.z); \ +} + + + +#define RECT(a) a.x, a.y, a.xl, a.yl + +/* Merges two rects +** Parameters: (rect_t x rect_t) a,b: The two rects to merge +** Returns : (rect_t) The smallest rect containing both a and b +*/ +static inline rect_t +gfx_rects_merge(rect_t a, rect_t b) +{ + rect_t retval; + MERGE_PARTIAL(x, xl); + MERGE_PARTIAL(y, yl); + return retval; +} +#undef MERGE_PARTIAL +#undef SUBMERGE_PARTIAL + + +/* Subset predicate for rectangles +** Parameters: (rect_t) a, b: The two rects to compare +** Returns : non-zero iff for each pixel p in a the following holds: p is in b. +*/ +static inline int +gfx_rect_subset(rect_t a, rect_t b) +{ + return ((a.x >= b.x) && (a.y >= b.y) + && ((a.x + a.xl) <= (b.x + b.xl)) + && ((a.y + a.yl) <= (b.y + b.yl))); +} + + +/* Equality predicate for rects +** Parameters: (rect_t) a, b +** Returns : (int) gfx_rect_subset(a,b) AND gfx_rect_subset(b,a) +*/ +static inline int +gfx_rect_equals(rect_t a, rect_t b) +{ + return (a.x == b.x + && a.xl == b.xl + && a.y == b.y + && a.yl == b.yl); +} + + +/* gfx_rect_fullscreen is declared in gfx/gfx_tools.c */ +extern rect_t gfx_rect_fullscreen; + +/*** points ***/ + +typedef struct { + int x, y; +} point_t; + +#define GFX_PRINT_POINT(p) (p).x, (p).y + +/* Generates a point_t from index data +** Parameters: (int x int) x,y: Indicated point +** Returns : (point_t) The resulting structure +*/ +static inline point_t +gfx_point(int x, int y) +{ + point_t point; + + point.x = x; + point.y = y; + + return point; +} + +/* Translation operation for rects +** Parameters: (rect_t) rect: The rect to translate +** (point_t) offset: The offset to translate it by +** Returns : (rect_t) The translated rect +*/ +static inline rect_t +gfx_rect_translate(rect_t rect, point_t offset) +{ + rect.x += offset.x; + rect.y += offset.y; + + return rect; +} + +#define GFX_RESID_NONE -1 + +#define GFX_PIC_COLORS 256 + +#define GFX_PIXMAP_FLAG_SCALED_INDEX (1<<0) /* Index data is scaled already */ +#define GFX_PIXMAP_FLAG_EXTERNAL_PALETTE (1<<1) /* The colors pointer points to an external palette */ +#define GFX_PIXMAP_FLAG_INSTALLED (1<<2) /* Pixmap has been registered */ +#define GFX_PIXMAP_FLAG_PALETTE_ALLOCATED (1<<3) /* Palette has been allocated */ +#define GFX_PIXMAP_FLAG_PALETTE_SET (1<<4) /* Palette has been propagated to the driver */ +#define GFX_PIXMAP_FLAG_DONT_UNALLOCATE_PALETTE (1<<5) /* Used by text, which uses preallocated colors */ +#define GFX_PIXMAP_FLAG_PALETTIZED (1<<6) /* Indicates a palettized view */ + +#define GFX_PIXMAP_COLOR_KEY_NONE -1 /* No transpacency colour key */ + +typedef struct { /* gfx_pixmap_t: Pixel map */ + + /*** Meta information ***/ + int ID; /* Resource ID, or GFX_RESID_NONE for anonymous graphical data */ + short loop, cel; /* loop and cel number for views */ + + + /*** Color map ***/ + int colors_nr; + gfx_pixmap_color_t *colors; /* colors_nr color entries, or NULL if the + ** default palette is to be used. + ** A maximum of 255 colors is allowed; color + ** index 0xff is reserved for transparency. + ** As a special exception, 256 colors are + ** allowed for background pictures (which do + ** not use transparency) + */ + unsigned int flags; + + /*** Hot spot ***/ + int xoffset, yoffset; /* x and y coordinates of the 'hot spot' (unscaled) */ + + /*** Index data ***/ + int index_xl, index_yl; /* width and height of the indexed original image */ + byte *index_data; /* Color-index data, or NULL if read from an + ** external source + */ + + /*** Drawable data ***/ + int xl, yl; /* width and height of the actual image */ + int data_size; /* Amount of allocated memory */ + byte *data; /* Drawable data, or NULL if not converted. */ + + byte *alpha_map; /* Byte map with alpha values. It is used only if the + ** graphics mode's alpha_mask is zero. + */ + + int color_key; + + /*** Data reserved for gfx_driver use ***/ + struct pixmap_internal { /* Internal state management data for use by drivers */ + int handle; /* initialized to 0 */ + void *info; /* initialized to NULL */ + } internal; + +} gfx_pixmap_t; + + +#define GFX_FONT_BUILTIN_5x8 -1 +#define GFX_FONT_BUILTIN_6x10 -2 + +typedef struct { /* gfx_bitmap_font_t: Bitmap font information */ + int ID; /* Unique resource ID */ + + int chars_nr; /* Numer of available characters */ + + int *widths; /* chars_nr character widths, in pixels */ + + int row_size; /* Byte size of each pixel row. For unscaled fonts, this is + ** always 1, 2, or 4. Otherwise, it's a multiple of 4. + */ + + int line_height; /* Height of each text line (usually identical to height) */ + int height; /* Height for all characters, in pixel rows */ + int char_size; /* Amount of memory occupied by one character in data */ + + byte *data; /* Font data, consisting of 'chars_nr' entries of 'height' rows + ** of 'row_size' bytes. For each character ch, its first byte + ** (the topmost row) is located at (data + (charsize * ch)), and + ** its pixel width is widths[ch], provided that (ch < chars_nr). + */ + +} gfx_bitmap_font_t; + + + +/***********************/ +/*** Constant values ***/ +/***********************/ + +/* Default palettes */ +extern gfx_pixmap_color_t gfx_sci0_image_colors[][16]; +extern gfx_pixmap_color_t gfx_sci0_pic_colors[256]; + +/* Return values */ +enum gfx_return_value_t { + GFX_OK = 0, /* Indicates "operation successful" */ + GFX_ERROR = -1, /* Indicates "operation failed" */ + GFX_FATAL = -2 +/* Fatal error: Used by graphics drivers to indicate that they were unable to +** do anything useful +*/ +}; + + +typedef enum {/* Map masks */ + GFX_MASK_NONE = 0, + GFX_MASK_VISUAL = 1, + GFX_MASK_PRIORITY = 2, + GFX_MASK_CONTROL = 4 +} gfx_map_mask_t; + +/* 'no priority' mode */ +#define GFX_NO_PRIORITY -1 + +/* Text alignment values */ + +typedef enum { + ALIGN_RIGHT = -1, + ALIGN_TOP = -1, + ALIGN_CENTER = 1, + ALIGN_LEFT = 0, + ALIGN_BOTTOM = 0 +} gfx_alignment_t; + + +typedef enum { + GFX_LINE_MODE_CORRECT, /* Scaled separately */ + GFX_LINE_MODE_FAST, /* Scaled by (xfact+yfact)/2 */ + GFX_LINE_MODE_FINE /* Always drawn at width 1 */ +} gfx_line_mode_t; + +typedef enum { + GFX_BRUSH_MODE_SCALED, /* Just scale the brush pixels */ + GFX_BRUSH_MODE_ELLIPSES, /* Replace pixels with ellipses */ + GFX_BRUSH_MODE_RANDOM_ELLIPSES, /* Replace pixels with ellipses moved and re-scaled randomly */ + GFX_BRUSH_MODE_MORERANDOM /* Distribute randomly */ +} gfx_brush_mode_t; + + +typedef enum { + GFX_LINE_STYLE_NORMAL, + GFX_LINE_STYLE_STIPPLED +} gfx_line_style_t; + + +typedef enum { + GFX_SHADE_FLAT, /* Don't shade */ + GFX_SHADE_VERTICALLY, /* Shade vertically */ + GFX_SHADE_HORIZONTALLY /* Shade horizontally */ +} gfx_rectangle_fill_t; + + +typedef enum { + GFX_COLOR_MODE_AUTO = 0, /* Auto-detect- handled by the gfxop library */ + GFX_COLOR_MODE_INDEX = 1, /* Index mode */ + GFX_COLOR_MODE_HIGH = 2, /* High color mode (15bpp or 16 bpp) */ + GFX_COLOR_MODE_TRUE = 4 /* True color mode (24 bpp padded to 32 bpp) */ +} gfx_color_mode_t; + +#endif /* !_SCI_GFX_SYSTEM_ */ diff --git a/engines/sci/include/gfx_tools.h b/engines/sci/include/gfx_tools.h new file mode 100644 index 0000000000..65fc5cba72 --- /dev/null +++ b/engines/sci/include/gfx_tools.h @@ -0,0 +1,293 @@ +/*************************************************************************** + gfx_tools.h Copyright (C) 2000 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* FreeSCI 0.3.1+ graphics subsystem helper functions */ + + +#ifndef _GFX_TOOLS_H_ +#define _GFX_TOOLS_H_ + +#include +#include + +typedef enum { + GFX_XLATE_FILTER_NONE, + GFX_XLATE_FILTER_LINEAR, + GFX_XLATE_FILTER_TRILINEAR +} gfx_xlate_filter_t; + +typedef enum { + GFXR_ANTIALIASING_NONE, + GFXR_ANTIALIASING_SIMPLE +} gfxr_antialiasing_t; + + +extern DLLEXTERN int gfx_crossblit_alpha_threshold; /* Crossblitting functions use this value as threshold + ** for distinguishing between transparent and opaque + ** wrt alpha values */ + +gfx_mode_t * +gfx_new_mode(int xfact, int yfact, int bytespp, unsigned int red_mask, unsigned int green_mask, + unsigned int blue_mask, unsigned int alpha_mask, int red_shift, int green_shift, + int blue_shift, int alpha_shift, int palette, int flags); +/* Allocates a new gfx_mode_t structure with the specified parameters +** Parameters: (int x int) xfact x yfact: Horizontal and vertical scaling factors +** (int) bytespp: Bytes per pixel +** (unsigned int) red_mask: Red bit mask +** (unsigned int) green_mask: Green bit mask +** (unsigned int) blue_mask: Blue bit mask +** (unsigned int) Alpha_mask: Alpha bit mask, or 0 if the alpha channel is not supported +** (int) red_shift: Red shift value +** (int) green_shift: Green shift value +** (int) blue_shift: Blue shift value +** (int) alpha_shift: Alpha shift value +** (int) palette: Number of palette colors, 0 if we're not in palette mode +** (int) flags: GFX_MODE_FLAG_* values ORred together, or just 0 +** Returns : (gfx_mode_t *) A newly allocated gfx_mode_t structure +*/ + + +void +gfx_clip_box_basic(rect_t *box, int maxx, int maxy); +/* Clips a rect_t +** Parameters: (rect_t *) box: Pointer to the box to clip +** (int x int) maxx, maxy: Maximum allowed width and height +** Returns : (void) +*/ + + +void +gfx_free_mode(gfx_mode_t *mode); +/* Frees all memory allocated by a mode structure +** Parameters: (gfx_mode_t *) mode: The mode to free +** Returns : (void) +*/ + + +gfx_pixmap_t * +gfx_new_pixmap(int xl, int yl, int resid, int loop, int cel); +/* Creates a new pixmap structure +** Parameters: (int x int) xl x yl: The dimensions (in SCI coordinates) of the pixmap +** (int) resid: The pixmap's resource ID, or GFX_RESID_NONE +** (int) loop: For views: The pixmap's loop number +** (int) cel: For cels: The pixmap's cel number +** Returns : (gfx_pixmap_t *) The newly allocated pixmap +** The following fiels are initialized: +** ID, loop, cel, index_xl, index_yl, xl, yl, data <- NULL, +** alpha_map <- NULL, internal.handle <- 0, internal.info <- NULL, colors <- NULL, +** index_scaled <- 0 +*/ + +gfx_pixmap_t * +gfx_clone_pixmap(gfx_pixmap_t *pixmap, gfx_mode_t *mode); +/* Clones a pixmap, minus its index data, palette and driver-specific handles +** Parameters: (gfx_pixmap_t *) pixmap: The pixmap to clone +** (gfx_mode_t *) mode: The mode to be applied to the pixmap +** Returns : (gfx_pixmap_t *) The clone +*/ + + +gfx_pixmap_t * +gfx_pixmap_alloc_index_data(gfx_pixmap_t *pixmap); +/* Allocates the index_data field of a pixmap +** Parameters: (gfx_pixmap_t *) pixmap: The pixmap to allocate for +** Returns : (gfx_pixmap_t *) pixmap +*/ + +gfx_pixmap_t * +gfx_pixmap_free_index_data(gfx_pixmap_t *pixmap); +/* Frees the index_data field of a pixmap +** Parameters: (gfx_pixmap_t *) pixmap: The pixmap to modify +** Returns : (gfx_pixmap_t *) pixmap +*/ + +gfx_pixmap_t * +gfx_pixmap_alloc_data(gfx_pixmap_t *pixmap, gfx_mode_t *mode); +/* Allocates the data field of a pixmap +** Parameters: (gfx_pixmap_t *) pixmap: The pixmap to allocate for +** (gfx_mode_t *) mode: The mode the memory is to be allocated for +** Returns : (gfx_pixmap_t *) pixmap +*/ + +gfx_pixmap_t * +gfx_pixmap_free_data(gfx_pixmap_t *pixmap); +/* Frees the memory allocated for a pixmap's data field +** Parameters: (gfx_pixmap_t *) pixmap: The pixmap to modify +** Returns : (gfx_pixmap_t *) pixmap +*/ + +void +gfx_free_pixmap(gfx_driver_t *driver, gfx_pixmap_t *pxm); +/* Frees all memory associated with a pixmap +** Parameters: (gfx_driver_t *) driver: The driver the pixmap is to be removed from +** (gfx_pixmap_t *) pxm: The pixmap to free +** Returns : (void) +*/ + +void +gfx_draw_line_pixmap_i(gfx_pixmap_t *pxm, point_t start, point_t end, int color); +/* Draws a line to a pixmap's index data buffer +** Parameters: (gfx_pixmap_t *) pxm: The pixmap to draw to +** (point_t) start: Starting point of the line to draw +** (point_t) end: End point of the line to draw +** (int) color: The byte value to write +** Returns : (void) +** Remember, this only draws to the /index/ buffer, not to the drawable buffer. +** The line is not clipped. Invalid x, y, x1, y1 values will result in memory corruption. +*/ + +void +gfx_draw_line_buffer(byte *buffer, int linewidth, int pixelwidth, + point_t start, point_t end, unsigned int color); +/* Draws a line to a linear pixel buffer +** Parameters: (byte *) buffer: Pointer to the start of the buffer to draw to +** (int) linewidth: Number of bytes per pixel line in the buffer +** (int) pixelwidth: Number of bytes per pixel +** (point_t) start: Starting point of the line to draw +** (point_t) end: End point of the line to draw +** (rect_t) Coordinates: the line should be drawn to (must be clipped already) +** xl and yl describe relative offsets, as usual. +** (unsigned int) color: The color to draw (only the lowest 8 * pixelwidth bits are relevant) +** Returns : (void) +** This function assumes 1 <= pixelwidth <= 4 +*/ + +void +gfx_draw_box_pixmap_i(gfx_pixmap_t *pxm, rect_t box, int color); +/* Draws a filled rectangular area to a pixmap's index buffer +** Parameters: (gfx_pixmap_t *) pxm: The pixmap to draw to +** (rect_t) box: The box to fill +** (int) color: The color to use for drawing +** Returns : (void) +** This function only draws to the index buffer. +*/ + +void +gfx_copy_pixmap_box_i(gfx_pixmap_t *dest, gfx_pixmap_t *src, rect_t box); +/* Copies part of a pixmap to another pixmap, with clipping +** Parameters: (gfx_pixmap_t *) dest: The destination pixmap +** (gfx_pixmap_t *) src: The source pixmap +** (rect_t) box: The area to copy +** Returns : (void) +*/ + +void +gfx_xlate_pixmap(gfx_pixmap_t *pxm, gfx_mode_t *mode, gfx_xlate_filter_t filter); +/* Translates a pixmap's index data to drawable graphics data +** Parameters: (gfx_pixmap_t *) pxm: The pixmap to translate +** (gfx_mode_t *) mode: The mode according which to scale +** (gfx_xlate_filter_t) filter: How to filter the data +** Returns : (void) +*/ + +void +gfxr_antialiase(gfx_pixmap_t *pixmap, gfx_mode_t *mode, gfxr_antialiasing_t type); +/* Performs antialiasing on a pixmap +** Parameters: (gfx_pixmap_t *) pixmap: The pixmap to antialiase +** (gfx_mode_t *) mode: The current mode +** (gfxr_antialiasing_t) type: Antialiasing algorithm to use +** Returns : (void) +*/ + +#define GFX_CROSSBLIT_FLAG_DATA_IS_HOMED (1<<0) +/* Means that the first byte in the visual data refers to the +** point corresponding to (dest.x, dest.y) */ + +int +gfx_crossblit_pixmap(gfx_mode_t *mode, gfx_pixmap_t *pxm, int priority, + rect_t src_coords, rect_t dest_coords, byte *dest, + int dest_line_width, byte *priority_dest, + int priority_line_width, int priority_skip, + int flags); +/* Transfers the non-transparent part of a pixmap to a linear pixel buffer +** Parameters: (gfx_mode_t *) mode: The graphics mode of the target buffer +** (gfx_pixmap_t *) pxm: The pixmap to transfer +** (int priority): The pixmap's priority +** (rect_t) src_coords: The source coordinates within the pixmap +** (rect_t) dest_coords: The destination coordinates (no scaling) +** (byte *) dest: Memory position of the upper left pixel of the +** linear pixel buffer +** (int) dest_line_width: Byte offset of the very first pixel in the +** second line of the linear pixel buffer, +** relative to dest. +** (byte *) priority_dest: Destination buffer for the pixmap's priority +** values +** (int) priority_line_width: Byte offset of the first pixel in the +** second line of the priority buffer +** (int) priority_skip: Amount of bytes allocated by each priority value +** (int) flags: Any crossblit flags +** Returns : (int) GFX_OK, or GFX_ERROR if the specified mode was invalid or unsupported +** A 'linear buffer' in this context means a data buffer containing an entire +** screen (visual or priority), with fixed offsets between each data row, and +** linear access. +*/ + +int +gfx_alloc_color(gfx_palette_t *pal, gfx_pixmap_color_t *color); +/* Allocates a color entry for the specified pixmap color +** Parameters: (gfx_palette_t *) pal: The palette structure the color should be allocated in +** (gfx_pixmap_color_t *) color: The color to allocate +** Returns : (int) GFX_ERROR if any error occured, GFX_OK if the color could be mapped to an +** existing color or a positive value if a new color was allocated in the +** palette. +*/ + +int +gfx_free_color(gfx_palette_t *pal, gfx_pixmap_color_t *color); +/* Frees the color entry allocated for the specified pixmap color +** Parameters: (gfx_palette_t *) pal: The palette structure the color was previously allocated in +** (gfx_pixmap_color_t *) color: The color to free +** Returns : (int) GFX_ERROR if any error occured, GFX_OK otherwise +*/ + +gfx_pixmap_t * +gfx_pixmap_scale_index_data(gfx_pixmap_t *pixmap, gfx_mode_t *mode); +/* Scales the index data associated with a pixmap +** Parameters: (gfx_pixmap_t *) pixmap: The pixmap whose index data should be scaled +** (gfx_mode_t *) mode: The mode to scale it to +** Returns : (gfx_pixmap_t *) pixmap +*/ + + +#ifdef HAVE_ALPHA_EV6_SUPPORT + +extern int axp_have_mvi; /* set to 1 iff the local system has the MVI instruction set extension */ + +void +alpha_mvi_crossblit_32(byte *dest, byte *src, int bytes_per_dest_line, int bytes_per_src_line, + int xl, int yl, byte *alpha, int bytes_per_alpha_line, int bytes_per_alpha_pixel, + unsigned int alpha_test_mask, int alpha_shift); +/* Internal function for accellerated 32 bit cross-blitting on Alpha hardware */ + +void +alpha_mvi_crossblit_32_P(byte *dest, byte *src, int bytes_per_dest_line, int bytes_per_src_line, + int xl, int yl, byte *alpha, int bytes_per_alpha_line, int bytes_per_alpha_pixel, + unsigned int alpha_test_mask, int alpha_shift, + byte *priority_pos, int bytes_per_priority_line, int bytes_per_priority_pixel, int priority); +/* Internal function for accellerated 32 bit cross-blitting on Alpha hardware (with priority) */ +#endif /* __alpha__ */ + + +#endif /* !_GFX_TOOLS_H_ */ diff --git a/engines/sci/include/gfx_widgets.h b/engines/sci/include/gfx_widgets.h new file mode 100644 index 0000000000..9ea9352971 --- /dev/null +++ b/engines/sci/include/gfx_widgets.h @@ -0,0 +1,548 @@ +/*************************************************************************** + gfx_widgets.h Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Graphical state management */ + +#ifndef _GFX_WIDGETS_H_ +#define _GFX_WIDGETS_H_ + +#include + +/* Enable the next line to keep a list of pointers to all widgets, with up to the specified amount +** of members (/SLOW/) */ +/* #define GFXW_DEBUG_WIDGETS 2048 */ + +/* Our strategy for dirty rectangle management */ +#define GFXW_DIRTY_STRATEGY GFXOP_DIRTY_FRAMES_CLUSTERS + +/* Properly belongs in sci_widgets.h, but we need it here */ +#define WINDOW_FLAG_AUTO_RESTORE 0x2000000 + +/* Indicates that a Chrono-Port should not be created even if it doesn't exist. */ +#define GFXW_CHRONO_NO_CREATE 1 + +/* Indicates that non-topmost ports should be scanned for a Chrono-Port. */ +#define GFXW_CHRONO_NON_TOPMOST 2 + +/* Terminology +** +** Two special terms are used in here: /equivalent/ and /clear/. Their meanings +** in this context are as follows: +** +** /clear/: Clearing a widget means overwriting the space it occupies in the back +** buffer with data from the static buffer. This affects both the visual and the +** priority buffer, the static buffer (and any effect the widget may have had on +** it) is not touched. +** +** /equivalent/: Two Widgets A and B are equivalent if and only if either of the +** following conditions is met: +** a) Both A and B are text widgets, and they occupy the same bounding rectangle. +** b) Both A and B are dynview widgets, and they have the same unique ID +** Note that /equivalent/ is not really an equivalence relation- while it is ob- +** viously transitive and symmetrical, it is not reflexive (e.g. a box widget +** is not /equivalent/ to itself), although this might be a nice addition for the +** future. +*/ + + +/*********************************/ +/* Fundamental widget operations */ +/*********************************/ + + +#define GFXW(foo) ((gfxw_widget_t *) foo) +/* Typecast an arbitrary widget to gfxw_widget_t*. Might eventually be changed to do tests as well. */ + +#define GFXWC(foo) ((gfxw_container_t *) foo) +/* Typecasts a container widget to gfxw_container_widget_t *. */ + +/* gfxw_point_zero is declared in gfx/widgets.c */ +extern point_t gfxw_point_zero; + +/*********************/ +/* Widget operations */ +/*********************/ + +/* These are for documentation purposes only. The actual declarations are in +** gfx_state_internal.h. +** +** +** +** -- draw(gfxw_widget_t *self, point_t pos) +** Draws the widget. +** Parameters: (gfxw_widget_t *) self: self reference +** (point_t) pos: The position to draw to (added to the widget's +** internal position) +** Returns : (int) 0 +** The widget is drawn iff it is flagged as dirty. Invoking this operation on +** a container widget will recursively draw all of its contents. +** +** +** -- widfree(gfxw_widget_t *self) +** Frees all memory associated to the widget +** Parameters: (gfxw_widget_t *) self: self reference +** Returns : (int) 0 +** The widget automatically removes itself from its owner, if it has one. +** Invoking this operation on a container will recursively free all of its +** contents. +** +** +** -- tag(gfxw_widget_t *self) +** Tags the specified widget +** Parameters: (gfxw_widget_t *) self: self reference +** Returns : (int) 0 +** If invoked on a container widget, this will also tag all of the container's +** contents (but not the contents' contents!) +** +** +** -- print(gfxw_widget_t *self, int indentation) +** Prints a string representation of the widget with sciprintf +** Parameters: (gfxw_widget_t *) self: self reference +** (int) indentation: Number of double spaces to indent +** Returns ; (int) 0 +** Will recursively print all of the widget's contents if the widget contains +** further sub-widgets +** +** +** -- compare_to(gfxw_widget_t *self, gfxw_widget_t *other) +** Compares two compareable widgets by their screen position +** Parameters: (gfxw_widget_t *) self: self reference +** (gfxw_widget_t *) other: other widget +** Returns : (int) <0, 0, or >0 if other is, respectively, less than, equal +** to, or greater than self +** This comparison only applies to some widgets; compare_to(a,a)=0 is not +** guaranteed. It may be used for sorting for all widgets. +** +** +** -- equals(gfxw_widget_t *self, gfxw_widget_t *other) +** Compares two compareable widgets for equality +** Parameters: (gfxw_widget_t *) self: self reference +** (gfxw_widget_t *) other: other widget +** Returns : (int) 0 if the widgets are not equal, != 0 if they match +** This operation checks whether two widgets describe the same graphical data. +** It is used to determine whether a new widget should be discarded because it +** describes the same graphical data as an old widget that has already been +** drawn. For lists, it also checks whether all contents are in an identical +** order. +** +** +** -- should_replace(gfxw_widget_t *self, gfxw_widget_t *other) +** Compares two compareable widgets for equality +** Parameters: (gfxw_widget_t *) self: self reference +** (gfxw_widget_t *) other: other widget +** Returns : (int) 0 if 'self' should be kept, != 0 if it should be replaced +** by the 'other' +** When 'equals' returns true, this means that no new widget will be added. +** However, in some cases newer widgets may contain information that should +** cause the older widget to be removed nonetheless; this is indicated by this +** function. +** +** +** -- superarea_of(gfxw_widget_t *self, gfxw_widget_t *other) +** Tests whether drawing self after other would reduce all traces of other +** Parameters: (gfxw_widget_t *) self: self reference +** (gxfw_widget_t *) other: The widget to compare for containment +** Returns : (int) 1 if self is superarea_of other, 0 otherwise +** +** +** -- set_visual(gfxw_widget_t *self) +** Sets the visual for the widget +** Parameters: (gfxw_widget_t *) self: self reference +** Returns : (int) 0 +** This function is called by container->add() and need not be invoked explicitly. +** It also makes sure that dirty rectangles are passed to parent containers. +** +** +** +** ************************** +** ** Container operations ** +** ************************** +** +** +** -- free_tagged(gfxw_container_t *self) +** Frees all tagged resources in the container +** Parameters: (gfxw_container_t *) self: self reference +** Returns : (int) 0 +** The container itself is never freed in this way. +** +** +** -- free_contents(gfxw_container_t *self) +** Frees all resources contained in the container +** Parameters: (gfxw_container_t *) self: self reference +** Returns : (int) 0 +** +** +** -- add_dirty_abs(gfxw_container_t *self, rect_t dirty, int propagate) +** Adds a dirty rectangle to the container's list of dirty rects +** Parameters: (gfxw_container_t *) self: self reference +** (rect_t) dirty: The rectangular screen area that is to be flagged +** as dirty, absolute to the screen +** (int) propagate: Whether the dirty rect should be propagated to the +** widget's parents +** Returns : (int) 0 +** Transparent containers will usually pass this value to their next ancestor, +** because areas below them might have to be redrawn. +** The dirty rectangle management strategy is defined in this file in +** GFXW_DIRTY_STRATEGY. +** +** +** -- add_dirty_rel(gfxw_container_t *self, rect_t dirty, int propagate) +** Adds a dirty rectangle to the container's list of dirty rects +** Parameters: (gfxw_container_t *) self: self reference +** (rect_t) dirty: The rectangular screen area that is to be flagged +** as dirty, relative to the widget +** (int) propagate: Whether the dirty rect should be propagated to the +** widget's parents +** Returns : (int) 0 +** Transparent containers will usually pass this value to their next ancestor, +** because areas below them might have to be redrawn. +** The dirty rectangle management strategy is defined in this file in +** GFXW_DIRTY_STRATEGY. +** +** +** -- add(gfxw_container_t *self, gfxw_widget_t *widget) +** Adds a widget to the list of contained widgets +** Parameters: (gfxw_container_t *) self: self reference +** (gfxw_widget_t *) widget: The widget to add +** Returns : (int) 0 +** Sorted lists sort their content into the list rather than adding it to the +** end. +*/ + + +/***************************/ +/* Basic widget generation */ +/***************************/ + +/*-- Primitive types --*/ + +gfxw_box_t * +gfxw_new_box(gfx_state_t *state, rect_t area, gfx_color_t color1, gfx_color_t color2, gfx_box_shade_t shade_type); +/* Creates a new box +** Parameters: (gfx_state_t *) state: The (optional) state +** (rect_t) area: The box's dimensions, relative to its container widget +** (gfx_color_t) color1: The primary color +** (gfx_color_t) color1: The secondary color (ignored if shading is disabled) +** (gfx_box_shade_t) shade_type: The shade type for the box +** Returns : (gfxw_box_t *) The resulting box widget +** The graphics state- if non-NULL- is used here for some optimizations. +*/ + +gfxw_primitive_t * +gfxw_new_rect(rect_t rect, gfx_color_t color, gfx_line_mode_t line_mode, gfx_line_style_t line_style); +/* Creates a new rectangle +** Parameters: (rect_t) rect: The rectangle area +** (gfx_color_t) color: The rectangle's color +** (gfx_line_mode_t) line_mode: The line mode for the lines that make up the rectangle +** (gfx_line_style_t) line_style: The rectangle's lines' style +** Returns : (gfxw_primitive_t *) The newly allocated rectangle widget (a Primitive) +*/ + +gfxw_primitive_t * +gfxw_new_line(point_t start, point_t end, gfx_color_t color, gfx_line_mode_t line_mode, gfx_line_style_t line_style); +/* Creates a new line +** Parameters: (point_t * point_t) (start, line): The line origin and end point +** (gfx_color_t) color: The line's color +** (gfx_line_mode_t) line_mode: The line mode to use for drawing +** (gfx_line_style_t) line_style: The line style +** Returns : (gfxw_primitive_t *) The newly allocated line widget (a Primitive) +*/ + + +/* Whether the view should be static */ +#define GFXW_VIEW_FLAG_STATIC (1 << 0) + +/* Whether the view should _not_ apply its x/y offset modifyers */ +#define GFXW_VIEW_FLAG_DONT_MODIFY_OFFSET (1 << 1) + +gfxw_view_t * +gfxw_new_view(gfx_state_t *state, point_t pos, int view, int loop, int cel, int palette, int priority, int control, + gfx_alignment_t halign, gfx_alignment_t valign, int flags); +/* Creates a new view (a cel, actually) +** Parameters: (gfx_state_t *) state: The graphics state +** (point_t) pos: The position to place the view at +** (int x int x int) view, loop, cel: The global cel ID +** (int) priority: The priority to use for drawing, or -1 for none +** (int) control: The value to write to the control map, or -1 for none +** (gfx_alignment_t x gfx_alignment_t) halign, valign: Horizontal and vertical +** cel alignment +** (int) flags: Any combination of GFXW_VIEW_FLAGs +** Returns : (gfxw_cel_t *) A newly allocated cel according to the specs +*/ + +gfxw_dyn_view_t * +gfxw_new_dyn_view(gfx_state_t *state, point_t pos, int z, int view, int loop, int cel, int palette, + int priority, int control, gfx_alignment_t halign, gfx_alignment_t valign, + int sequence); +/* Creates a new dyn view +** Parameters: (gfx_state_t *) state: The graphics state +** (point_t) pos: The position to place the dynamic view at +** (int) z: The z coordinate +** (int x int x int) view, loop, cel: The global cel ID +** (int) priority: The priority to use for drawing, or -1 for none +** (int) control: The value to write to the control map, or -1 for none +** (gfx_alignment_t x gfx_alignment_t) halign, valign: Horizontal and vertical +** cel alignment +** (int) sequence: Sequence number: When sorting dynviews, this number is +** considered last for sorting (ascending order) +** Returns : (gfxw_cel_t *) A newly allocated cel according to the specs +** Dynamic views are non-pic views with a unique global identifyer. This allows for drawing +** optimizations when they move or change shape. +*/ + +gfxw_text_t * +gfxw_new_text(gfx_state_t *state, rect_t area, int font, const char *text, gfx_alignment_t halign, + gfx_alignment_t valign, gfx_color_t color1, gfx_color_t color2, + gfx_color_t bgcolor, int flags); +/* Creates a new text widget +** Parameters: (gfx_state_t *) state: The state the text is to be calculated from +** (rect_t) area: The area the text is to be confined to (the yl value is only +** relevant for text aligment, though) +** (int) font: The number of the font to use +** (gfx_alignment_t x gfx_alignment_t) halign, valign: Horizontal and +** vertical text alignment +** (gfx_color_t x gfx_color_t) color1, color2: Text foreground colors (if not equal, +** The foreground is dithered between them) +** (gfx_color_t) bgcolor: Text background color +** (int) flags: GFXR_FONT_FLAGs, orred together (see gfx_resource.h) +** Returns : (gfxw_text_t *) The resulting text widget +*/ + +void +gfxw_text_info(gfx_state_t *state, gfxw_text_t *text, int *lines_nr, + int *lineheight, int *offset); +/* Determines text widget meta-information +** Parameters: (gfx_state_t *) state: The state to operate on +** (gfx_text_t *) text: The widget to query +** Returns : (int) lines_nr: Number of lines used in the text +** (int) lineheight: Pixel height (SCI scale) of each text line +** (int) offset: Pixel offset (SCI scale) of the space after the +** last character in the last line +*/ + +gfxw_widget_t * +gfxw_set_id(gfxw_widget_t *widget, int ID, int subID); +/* Sets a widget's ID +** Parmaeters: (gfxw_widget_t *) widget: The widget whose ID should be set +** (int x int) ID, subID: The ID to set +** Returns : (gfxw_widget_t *) widget +** A widget ID is unique within the container it is stored in, if and only if it was +** added to that container with gfxw_add(). +** This function handles widget = NULL gracefully (by doing nothing and returning NULL). +*/ + +gfxw_widget_t * +gfxw_remove_id(gfxw_container_t *container, int ID, int subID); +/* Finds a widget with a specific ID in a container and removes it from there +** Parameters: (gfxw_container_t *) container: The container to search in +** (int) ID: The ID to look for +** (int) subID: The subID to look for, or GFXW_NO_ID for any +** Returns : (gfxw_widget_t *) The resulting widget or NULL if no match was found +** Search is non-recursive; widgets with IDs hidden in subcontainers will not be found. +*/ + + +gfxw_dyn_view_t * +gfxw_dyn_view_set_params(gfxw_dyn_view_t *widget, int under_bits, void *under_bitsp, int signal, void *signalp); +/* Initializes a dyn view's interpreter attributes +** Parameters: (gfxw_dyn_view_t *) widget: The widget affected +** (int x void * x int x void *) under_bits, inder_bitsp, signal, signalp: Interpreter-dependant data +** Returns : (gfxw_dyn_view_t *) widget +*/ + +gfxw_widget_t * +gfxw_hide_widget(gfxw_widget_t *widget); +/* Makes a widget invisible without removing it from the list of widgets +** Parameters: (gfxw_widget_t *) widget: The widget to invisibilize +** Returns : (gfxw_widget_t *) widget +** Has no effect on invisible widgets +*/ + +gfxw_widget_t * +gfxw_show_widget(gfxw_widget_t *widget); +/* Makes an invisible widget reappear +** Parameters: (gfxw_widget_t *) widget: The widget to show again +** Returns : (gfxw_widget_t *) widget +** Does not affect visible widgets +*/ + +gfxw_widget_t * +gfxw_abandon_widget(gfxw_widget_t *widget); +/* Marks a widget as "abandoned" +** Parameters: (gfxw_widget_t *) widget: The widget to abandon +** Returns : (gfxw_widget_t *) widget +*/ + +/*-- Container types --*/ + +#define GFXW_LIST_UNSORTED 0 +#define GFXW_LIST_SORTED 1 + +gfxw_list_t * +gfxw_new_list(rect_t area, int sorted); +/* Creates a new list widget +** Parameters: (rect_t) area: The area covered by the list (absolute position) +** (int) sorted: Whether the list should be a sorted list +** Returns : (gfxw_list_t *) A newly allocated list widget +** List widgets are also referred to as Display Lists. +*/ + +gfxw_visual_t * +gfxw_new_visual(gfx_state_t *state, int font); +/* Creates a new visual widget +** Parameters: (gfx_state_t *) state: The graphics state +** (int) font: The default font number for contained ports +** Returns : (gfxw_list_t *) A newly allocated visual widget +** Visual widgets are containers for port widgets. +*/ + + +gfxw_port_t * +gfxw_new_port(gfxw_visual_t *visual, gfxw_port_t *predecessor, rect_t area, gfx_color_t fgcolor, gfx_color_t bgcolor); +/* Creates a new port widget with the default settings +** Paramaters: (gfxw_visual_t *) visual: The visual the port is added to +** (gfxw_port_t *) predecessor: The port's predecessor +** (rect_t) area: The screen area covered by the port (absolute position) +** (gfx_color_t) fgcolor: Foreground drawing color +** (gfx_color_t) bgcolor: Background color +** Returns : (gfxw_port_t *) A newly allocated port widget +** A port differentiates itself from a list in that it contains additional information, +** and an optional title (stored in a display list). +** Ports are assigned implicit IDs identifying their position within the port stack. +*/ + +gfxw_port_t * +gfxw_find_port(gfxw_visual_t *visual, int ID); +/* Retrieves a port with the specified ID +** Parameters: (gfxw_visual_t *) visual: The visual the port is to be retreived from +** (int) ID: The port's ID +** Returns : (gfxw_port_t *) The requested port, or NULL if it didn't exist +** This function is O(1). +*/ + +gfxw_port_t * +gfxw_find_default_port(gfxw_visual_t *visual); +/* Retreives the default port from a visual +** Parameters: (gfxw_visual_t *) visual: The visual the port should be retreived from +** Returns : (gfxw_port_t *) The default port, or NULL if no port is present +** The 'default port' is the last port to be instantiated; usually the topmost +** or highest-ranking port. +*/ + +void +gfxw_port_set_auto_restore(gfxw_visual_t *visual, gfxw_port_t *window, rect_t auto_rect); +/* Sets rectangle to be restored upon port removal +** Parameters: (state_t *) s: The state to operate on + (gfxw_port_t *) window: The affected window +** (rect_t) auto_rect: The area to restore +** Returns : (void) +*/ + +gfxw_port_t * +gfxw_remove_port(gfxw_visual_t *visual, gfxw_port_t *port); +/* Removes a port from a visual +** Parameters: (gfxw_visual_t *) visual: The visual the port should be removed from +** (gfxw_port_t *) port: The port to remove +** Returns : (gfxw_port_t *) port's parent port, or NULL if it had none +*/ + +void +gfxw_remove_widget_from_container(gfxw_container_t *container, gfxw_widget_t *widget); +/* Removes the widget from the specified port +** Parameters: (gfxw_container_t *) container: The container it should be removed from +** (gfxw_widget_t *) widget: The widget to remove +** Returns : (void) +*/ + +gfxw_snapshot_t * +gfxw_make_snapshot(gfxw_visual_t *visual, rect_t area); +/* Makes a "snapshot" of a visual +** Parameters: (gfxw_visual_t *) visual: The visual a snapshot is to be taken of +** (rect_t) area: The area a snapshot should be taken of +** Returns : (gfxw_snapshot_t *) The resulting, newly allocated snapshot +** It's not really a full qualified snaphot, though. See gfxw_restore_snapshot +** for a full discussion. +** This operation also increases the global serial number counter by one. +*/ + +int +gfxw_widget_matches_snapshot(gfxw_snapshot_t *snapshot, gfxw_widget_t *widget); +/* Predicate to test whether a widget would be destroyed by applying a snapshot +** Parameters: (gfxw_snapshot_t *) snapshot: The snapshot to test against +** (gfxw_widget_t *) widget: The widget to test +** Retunrrs : (int) An appropriate boolean value +*/ + +gfxw_snapshot_t * +gfxw_restore_snapshot(gfxw_visual_t *visual, gfxw_snapshot_t *snapshot); +/* Restores a snapshot to a visual +** Parameters: (gfxw_visual_t *) visual: The visual to operate on +** (gfxw_snapshot_t *) snapshot: The snapshot to restore +** Returns : (gfxw_snapshot_t *) snapshot (still needs to be freed) +** The snapshot is not really restored; only more recent widgets touching +** the snapshotted area are destroyed. +*/ + +void +gfxw_annihilate(gfxw_widget_t *widget); +/* As widget->widfree(widget), but destroys all overlapping widgets +** Parameters: (gfxw_widget_t *) widget: The widget to use +** Returns : (void) +** This operation calls widget->widfree(widget), but it also destroys +** all widgets with a higher or equal priority drawn after this widget. +*/ + +gfxw_dyn_view_t * +gfxw_picviewize_dynview(gfxw_dyn_view_t *dynview); +/* Turns a dynview into a picview +** Parameters: (gfxw_dyn_view_t *) dynview: The victim +** Returns : (gfxw_dyn_view_t *) The victim, after his transformation +** The only changes are in function and type variables, actually. +*/ + +void +gfxw_port_auto_restore_background(gfxw_visual_t *visual, gfxw_port_t *window, rect_t auto_rect); +/* Tags a window widget as automatically restoring the visual background upon removal +** Parameters: (gfx_visual_t *) visual: The base visual +** (gfxw_port_t *) window: The window to tag +** (rect_t) auto_rect: The background to remember +** Also records the specified background rectangle, for later recovery +*/ + + +gfxw_port_t * +gfxw_get_chrono_port(gfxw_visual_t *visual, gfxw_list_t **temp_widgets_list, int flags); + +void +gfxw_add_to_chrono(gfxw_visual_t *visual, gfxw_widget_t *widget); + +void +gfxw_widget_reparent_chrono(gfxw_visual_t *visual, gfxw_widget_t *view, gfxw_list_t *target); + +void +gfxw_widget_kill_chrono(gfxw_visual_t *visual, int window); + +#endif /* !_GFX_WIDGETS_H_ */ diff --git a/engines/sci/include/hashmap.h b/engines/sci/include/hashmap.h new file mode 100644 index 0000000000..f9130c4493 --- /dev/null +++ b/engines/sci/include/hashmap.h @@ -0,0 +1,132 @@ +/*************************************************************************** + hashmap.h Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Defines a hash map that maps values to small integers. +** Preprocessor parameters: +** TYPE: The type to hash +** HASH_MAX: Maximum hash value +** HASH(x): Hashes a value of type TYPE to an int from 0 to HASH_MAX +** COMP(x, y): Compares x, y, returns zero(!) iff they are equal +** MUST_FREE: Define if 'lost' values are simple pointers and should be freed +** DUPLICATOR: Usually needed when MUST_FREE is being used, means that +** new values must be dup'd before being added. +** Define it to the name of the function to use for duplication +** (e.g. '#define DUPLICATOR strdup'). +*/ + + /**-- WARNING!!! --**/ + +/* This file uses a lot of dark magic (aka preprocessor macros) to define +** its semantics. It is not to be taken lightly. It is not to be taken +** as an example for good programming. Most importantly, it is not to +** be read by mere mortals, except for the author and other people who +** have undergone appropriate psychological preparation. Keep out of +** reach of small children. Do not shake. Do not put in a microwave. Do +** not feed to animals. +*/ + +/* OK, it's not that bad. */ + + +#ifndef HASH_MAX +# error "Must define the maximum hash value (HASH_MAX) before including hashmap.h!" +#endif + +#ifndef HASH +# error "Must define the hash function (HASH) before including hashmap.h!" +#endif + +#ifndef COMP +# error "Must define a comparison function (COMP) before including hashmap.h!" +#endif + + +#define DECLARE_STRUCTS(TYPE) \ +typedef struct _##TYPE##_hash_map_node { \ + TYPE name; \ + int value; \ + struct _##TYPE##_hash_map_node *next; \ +} TYPE##_hash_map_node_t; \ + \ + \ +typedef struct { \ + int base_value; /* Starts at zero, counts upwards */ \ + TYPE##_hash_map_node_t *nodes[HASH_MAX+1]; \ + TYPE##_hash_map_node_t *holes; /* List of freed entries to minimize \ + ** memory operations and modifications \ + ** to base_value */ \ +} TYPE##_hash_map_t, *TYPE##_hash_map_ptr; + + +#define DECLARE_FUNCTIONS(TYPE) \ + \ +TYPE##_hash_map_t * \ +new_##TYPE##_hash_map(void); \ +/* Creates a new hash map for the specified TYPE \ +** Parameters: (void) \ +** Returns : (TYPE##_hash_map_t *) The newly allocated hash map \ +*/ \ + \ +void \ +free_##TYPE##_hash_map(TYPE##_hash_map_ptr map); \ +/* Frees the specified hash map \ +** Parameters: (TYPE##_hash_map_t *) map: The map to free \ +** Returns : (void) \ +*/ \ + \ +void \ +apply_to_##TYPE##_hash_map(TYPE##_hash_map_ptr map, void *param, void (*note) (void *param, TYPE name, int value)); \ +/* Iterates over all entries in the hash map and invokes 'note' \ +** Parameters: (TYPE##_hash_map_t *) map: The map to iterate over \ +** (void *) param: Some parameter to pass to 'note' \ +** ((voidptr * TYPE * value) -> void) note: The callback to invoke for each entry \ +*/ \ + \ +int \ +TYPE##_hash_map_check_value(TYPE##_hash_map_ptr map, TYPE value, char add, char *was_added); \ +/* Checks whether a value is in the map, adds it if neccessary \ +** Parameters: (TYPE##_hash_map_t *) map: The map to look in/modify \ +** (TYPE) value: The value to check for/add \ +** (char) add: Whether to add the value if it's not in there \ +** (char *) was_added: Set to non-zero iff the value is new \ +** Ignored if NULL. \ +** Returns : (int) The new (or old) index, or -1 if add was zero and \ +** the value couldn't be found \ +** If MUST_FREE is defined and add is set but the value was already in \ +** there, the value is freed. \ +*/ \ + \ +int \ +TYPE##_hash_map_remove_value(TYPE##_hash_map_ptr map, TYPE value); \ +/* Removes a value from the hash map \ +** Parameters: (TYPE##_hash_map_t *) map: The map to remove from \ +** (TYPE) value: The value to remove \ +** Returns : (int) The ID of the value, or -1 if it wasn't presen \ +*/ + + + + diff --git a/engines/sci/include/heapmgr.h b/engines/sci/include/heapmgr.h new file mode 100644 index 0000000000..77583e1f39 --- /dev/null +++ b/engines/sci/include/heapmgr.h @@ -0,0 +1,120 @@ +/*************************************************************************** + heapmgr.h Copyright (C) 2002 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Heap-like managed structure */ + +#ifndef _FREESCI_HEAPMGR_H_ +#define _FREESCI_HEAPMGR_H_ + +#include +#include + +#define HEAPENTRY_INVALID -1 + +#define ENTRY_IS_VALID(t, i) ((i) >= 0 && (i) < (t)->max_entry && (t)->table[(i)].next_free == (i)) + +#define DECLARE_HEAPENTRY(ENTRY) \ +typedef struct { \ + int next_free; /* Only used for free entries */ \ + ENTRY##_t entry; \ +} ENTRY##_entry_t; \ + \ +typedef struct { \ + int entries_nr; /* Number of entries allocated */ \ + int first_free; /* Beginning of a singly linked list for entries */ \ + int entries_used; /* Statistical information */ \ + int max_entry; /* Highest entry used */ \ + ENTRY##_entry_t *table; \ +} ENTRY##_table_t; \ + \ +void \ +init_##ENTRY##_table(ENTRY##_table_t *table); \ +int \ +alloc_##ENTRY##_entry(ENTRY##_table_t *table); \ +void \ +free_##ENTRY##_entry(ENTRY##_table_t *table, int index); + + + +#define DEFINE_HEAPENTRY_WITH_CLEANUP(ENTRY, INITIAL, INCREMENT, CLEANUP_FN) \ +void \ +init_##ENTRY##_table(ENTRY##_table_t *table) \ +{ \ + table->entries_nr = INITIAL; \ + table->max_entry = 0; \ + table->entries_used = 0; \ + table->first_free = HEAPENTRY_INVALID; \ + table->table = (ENTRY##_entry_t*)sci_malloc(sizeof(ENTRY##_entry_t) * INITIAL);\ + memset(table->table, 0, sizeof(ENTRY##_entry_t) * INITIAL); \ +} \ + \ +void \ +free_##ENTRY##_entry(ENTRY##_table_t *table, int index) \ +{ \ + ENTRY##_entry_t *e = table->table + index; \ + \ + if (index < 0 || index >= table->max_entry) { \ + fprintf(stderr, "heapmgr: Attempt to release" \ + " invalid table index %d!\n", index); \ + BREAKPOINT(); \ + } \ + CLEANUP_FN(&(e->entry)); \ + \ + e->next_free = table->first_free; \ + table->first_free = index; \ + table->entries_used--; \ +} \ + \ +int \ +alloc_##ENTRY##_entry(ENTRY##_table_t *table) \ +{ \ + table->entries_used++; \ + if (table->first_free != HEAPENTRY_INVALID) { \ + int oldff = table->first_free; \ + table->first_free = table->table[oldff].next_free; \ + \ + table->table[oldff].next_free = oldff; \ + return oldff; \ + } else { \ + if (table->max_entry == table->entries_nr) { \ + table->entries_nr += INCREMENT; \ + \ + table->table = (ENTRY##_entry_t*)sci_realloc(table->table,\ + sizeof(ENTRY##_entry_t) \ + * table->entries_nr); \ + memset(&table->table[table->entries_nr-INCREMENT], \ + 0, INCREMENT*sizeof(ENTRY##_entry_t)); \ + } \ + table->table[table->max_entry].next_free = \ + table->max_entry; /* Tag as 'valid' */ \ + return table->max_entry++; \ + } \ +} + +#define _HEAPENTRY_IGNORE_ME(x) +#define DEFINE_HEAPENTRY(e, i, p) DEFINE_HEAPENTRY_WITH_CLEANUP(e, i, p, _HEAPENTRY_IGNORE_ME) + +#endif /* !_FREESCI_HEAPMGR_H_ */ diff --git a/engines/sci/include/int_hashmap.h b/engines/sci/include/int_hashmap.h new file mode 100644 index 0000000000..12630b6198 --- /dev/null +++ b/engines/sci/include/int_hashmap.h @@ -0,0 +1,63 @@ +/*************************************************************************** + int_hashmap.h Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef _INT_HASHMAP_H_ +#define _INT_HASHMAP_H_ + +/* Assumes that the ints are relatively evenly distributed */ + +#define DCS_INT_HASH_MAX 255 + +#define HASH_MAX DCS_INT_HASH_MAX +#define COMP(x, y) ((x)-(y)) +#define HASH(x) (x & 0xff) +#undef MUST_FREE + +#include "hashmap.h" +DECLARE_STRUCTS(int) +DECLARE_FUNCTIONS(int) + +#ifndef BUILD_MAP_FUNCTIONS +# undef HASH_MAX +# undef COMP +# undef HASH +#endif + +/* see hashmap.h for descriptions of these functions */ +int_hash_map_ptr +new_int_hash_map(void); + +void +free_int_hash_map(int_hash_map_ptr); + +int +int_hash_map_check_value(int_hash_map_ptr, int value, char add_p, char *added); + +int +int_hash_map_remove_value(int_hash_map_ptr, int value); + +#endif /* _INT_HASHMAP_H_ */ diff --git a/engines/sci/include/kdebug.h b/engines/sci/include/kdebug.h new file mode 100644 index 0000000000..9c69126e41 --- /dev/null +++ b/engines/sci/include/kdebug.h @@ -0,0 +1,121 @@ +/*************************************************************************** + kdebug.h Copyright (C) 1999,2000,01 Christoph Reichenbach, TU Darmstadt + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ +/* Kernel debug defines */ + +#ifndef _SCI_KDEBUG_H_ +#define _SCI_KDEBUG_H_ + +struct _state; +#define SCIk_DEBUG_MODES 18 + +#define SCIkERROR_NR -2 +#define SCIkWARNING_NR -1 +#define SCIkSTUB_NR 0 +#define SCIkFUNCCHK_NR 5 +#define SCIkSOUNDCHK_NR 7 +#define SCIkGFXDRIVER_NR 8 +#define SCIkBASESETTER_NR 9 +#define SCIkPARSER_NR 10 +#define SCIkAVOIDPATH_NR 17 + +#define SCIkERROR s, __FILE__, __LINE__, SCIkERROR_NR +#define SCIkWARNING s, __FILE__, __LINE__, SCIkWARNING_NR +#define SCIkSTUB s, __FILE__, __LINE__, SCIkSTUB_NR +#define SCIkNODES s, __FILE__, __LINE__, 1 +#define SCIkGRAPHICS s, __FILE__, __LINE__, 2 +#define SCIkSTRINGS s, __FILE__, __LINE__, 3 +#define SCIkMEM s, __FILE__, __LINE__, 4 +#define SCIkFUNCCHK s, __FILE__, __LINE__, SCIkFUNCCHK_NR +#define SCIkBRESEN s, __FILE__, __LINE__, 6 +#define SCIkSOUND s, __FILE__, __LINE__, SCIkSOUNDCHK_NR +#define SCIkGFXDRIVER s, __FILE__, __LINE__, SCIkGFXDRIVER_NR +#define SCIkBASESETTER s, __FILE__, __LINE__, SCIkBASESETTER_NR +#define SCIkPARSER s, __FILE__, __LINE__, SCIkPARSER_NR +#define SCIkMENU s, __FILE__, __LINE__, 11 +#define SCIkSAID s, __FILE__, __LINE__, 12 +#define SCIkFILE s, __FILE__, __LINE__, 13 +#define SCIkTIME s, __FILE__, __LINE__, 14 +#define SCIkROOM s, __FILE__, __LINE__, 15 +#define SCIkEMU s, __FILE__, __LINE__, 16 +#define SCIkAVOIDPATH s, __FILE__, __LINE__, SCIkAVOIDPATH_NR + +#define SCI_KERNEL_DEBUG + +#ifdef SCI_KERNEL_DEBUG + +#ifdef __GNUC__ + +#define SCIkdebug(arguments...) _SCIGNUkdebug(__PRETTY_FUNCTION__, ## arguments) + +#else /* !__GNUC__ */ + +#define SCIkdebug _SCIkdebug + +#endif /* !__GNUC__ */ + +#else /* !SCI_KERNEL_DEBUG */ + +#define SCIkdebug 1? (void)0 : _SCIkdebug + +#endif /* !SCI_KERNEL_DEBUG */ + + + +#ifdef __GNUC__ + +#define SCIkwarn(arguments...) _SCIGNUkdebug(__PRETTY_FUNCTION__, ## arguments) + +#else /* !__GNUC__ */ + +#define SCIkwarn _SCIkwarn + +#endif /* !__GNUC__ */ + +/* Internal functions */ +void +_SCIkwarn(struct _state *s, const char *file, int line, int area, const char *format, ...); +void +_SCIkdebug(struct _state *s, const char *file, int line, int area, const char *format, ...); +void +_SCIGNUkdebug(const char *funcname, struct _state *s, const char *file, int line, int area, const char *format, ...); + +/* If mode=1, enables debugging for specified areas. If mode=0, disables +** debugging for specified areas. +** Valid area characters: ulgcmfbad +*/ + +void +set_debug_mode (struct _state *s, int mode, const char *areas); + +extern int sci_debug_flags; + +/* Debug flags */ +#define _DEBUG_FLAG_LOGGING 1 /* Log each command executed */ +#define _DEBUG_FLAG_BREAK_ON_WARNINGS 2 /* Break on warnings */ + + +#endif diff --git a/engines/sci/include/kernel.h b/engines/sci/include/kernel.h new file mode 100644 index 0000000000..3b675dac65 --- /dev/null +++ b/engines/sci/include/kernel.h @@ -0,0 +1,403 @@ +/*************************************************************************** + kernel.h Copyright (C) 1999..2002 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + +#ifndef _SCI_KERNEL_H_ +#define _SCI_KERNEL_H_ + +#include +#include +#include +#include +#include +#include /* sciprintf() */ + +#ifdef HAVE_FNMATCH_H +#include +#endif /* HAVE_FNMATCH_H */ + +#ifdef _MSC_VER +# include +# include +#endif + +#ifndef PI +# define PI 3.14159265358979323846 +#endif /* !PI */ + +extern int _kdebug_cheap_event_hack; +extern int _kdebug_cheap_soundcue_hack; +extern int stop_on_event; + +extern DLLEXTERN int _debug_seeking; +extern DLLEXTERN int _debug_step_running; + +#define AVOIDPATH_DYNMEM_STRING "AvoidPath polyline" + + +typedef struct { + int x, y, xend, yend; +} abs_rect_t; + +/* Formerly, the heap macros were here; they have been deprecated, however. */ + +/******************** Selector functionality ********************/ + +#define GET_SEL32(_o_, _slc_) read_selector(s, _o_, s->selector_map._slc_, __FILE__, __LINE__) +#define GET_SEL32V(_o_, _slc_) (GET_SEL32(_o_, _slc_).offset) +#define GET_SEL32SV(_o_, _slc_) ((gint16)(GET_SEL32(_o_, _slc_).offset)) +/* Retrieves a selector from an object +** Parameters: (reg_t) object: The address of the object which the selector should be read from +** (selector_name) selector: The selector to read +** Returns : (gint16/guint16/reg_t) The selector value +** This macro halts on error. 'selector' must be a selector name registered in vm.h's +** selector_map_t and mapped in script.c. +*/ + +#define PUT_SEL32(_o_, _slc_, _val_) write_selector(s, _o_, s->selector_map._slc_, _val_, __FILE__, __LINE__) +#define PUT_SEL32V(_o_, _slc_, _val_) write_selector(s, _o_, s->selector_map._slc_, make_reg(0, _val_), __FILE__, __LINE__) +/* Writes a selector value to an object +** Parameters: (reg_t) object: The address of the object which the selector should be written to +** (selector_name) selector: The selector to read +** (gint16) value: The value to write +** Returns : (void) +** This macro halts on error. 'selector' must be a selector name registered in vm.h's +** selector_map_t and mapped in script.c. +*/ + + +#define INV_SEL(_object_, _selector_, _noinvalid_) \ + s, _object_, s->selector_map._selector_, _noinvalid_, funct_nr, argv, argc, __FILE__, __LINE__ +/* Kludge for use with invoke_selector(). Used for compatibility with compilers that can't +** handle vararg macros. +*/ + + +reg_t +read_selector(struct _state *s, reg_t object, selector_t selector_id, const char *fname, int line); +void +write_selector(struct _state *s, reg_t object, selector_t selector_id, reg_t value, + const char *fname, int line); +int +invoke_selector(struct _state *s, reg_t object, int selector_id, int noinvalid, int kfunct, + stack_ptr_t k_argp, int k_argc, const char *fname, int line, int argc, ...); + + + + +/******************** Text functionality ********************/ +char * +kernel_lookup_text(struct _state *s, reg_t address, int index); +/* Looks up text referenced by scripts +** Parameters: (state_t *s): The current state +** (reg_t) address: The address to look up +** (int) index: The relative index +** Returns : (char *): The referenced text, or NULL on error. +** SCI uses two values to reference to text: An address, and an index. The address +** determines whether the text should be read from a resource file, or from the heap, +** while the index either refers to the number of the string in the specified source, +** or to a relative position inside the text. +*/ + + + +/******************** Debug functionality ********************/ +#define KERNEL_OOPS(reason) kernel_oops(s, __FILE__, __LINE__, reason) + +/* Non-fatal assertion */ +#define SCIkASSERT(a) if (!(a)) { \ + SCIkwarn(SCIkERROR, "Assertion " #a " failed in " __FILE__ " line %d\n", __LINE__); \ + return; \ +} + +#ifdef SCI_KERNEL_DEBUG + +#define CHECK_THIS_KERNEL_FUNCTION if (s->debug_mode & (1 << SCIkFUNCCHK_NR)) {\ + int i;\ + sciprintf("Kernel CHECK: %s[%x](", s->kernel_names[funct_nr], funct_nr); \ + for (i = 0; i < argc; i++) { \ + sciprintf("%04x", 0xffff & UKPV(i)); \ + if (i+1 < argc) sciprintf(", "); \ + } \ + sciprintf(")\n"); \ +} \ + +#else /* !SCI_KERNEL_DEBUG */ + +#define CHECK_THIS_KERNEL_FUNCTION + +#endif /* !SCI_KERNEL_DEBUG */ + + +int +listp(struct _state *s, reg_t address); +/* Determines whether the object at
is a list +** Parameters: (state_t *) s: The state to use +** (reg_t) address: The address to check +** Returns : (int) 0 if not, non-zero if it is a list. +*/ + +int +is_object(struct _state *s, reg_t obj); +/* Checks whether a heap address contains an object +** Parameters: (state_t *) s: The current state +** (reg_t) obj: The address to check +** Returns : (int) 1 if it is an object, 0 otherwise +*/ + + +/* Functions for internal macro use */ +void +_SCIkvprintf(FILE *file, const char *format, va_list args); +void +_SCIkprintf(FILE *file, const char *format, ...); + + + + + +/******************** Kernel function parameter macros ********************/ + +/* Returns the parameter value or (alt) if not enough parameters were supplied */ + + +#define KP_ALT(x, alt) ((x < argc)? argv[x] : (alt)) +#define KP_UINT(x) ((guint16) x.offset) +#define KP_SINT(x) ((gint16) x.offset) + + +#define SKPV(x) KP_SINT(argv[x]) +#define UKPV(x) KP_UINT(argv[x]) +#define SKPV_OR_ALT(x,a) KP_SINT(KP_ALT(x, make_reg(0, a))) +#define UKPV_OR_ALT(x,a) KP_UINT(KP_ALT(x, make_reg(0, a))) + +reg_t * +kernel_dereference_reg_pointer(struct _state *s, reg_t pointer, int entries); +byte * +kernel_dereference_bulk_pointer(struct _state *s, reg_t pointer, int entries); +#define kernel_dereference_char_pointer(state, pointer, entries) (char*)kernel_dereference_bulk_pointer(state, pointer, entries) +/* Dereferences a heap pointer +** Parameters: (state_t *) s: The state to operate on +** (reg_t ) pointer: The pointer to dereference +** (int) entries: The number of values expected (for checking) +** (use 0 for strings) +** Returns : (reg_t/char *): A physical reference to the address pointed +** to, or NULL on error or if not enugh entries +** were available +** reg_t dereferenciation also assures alignedness of data. +*/ + +/******************** Resource Macros ********************/ + +/* Returns the composite resource ID: */ +#define RESOURCE_ID(type, number) (number) | ((type) << 11) +#define RESOURCE_NUMBER(resid) ((resid) & 0x7ff) +#define RESOURCE_TYPE(resid) ((resid) >> 11) + + + + + +int +kernel_oops(struct _state *s, const char *file, int line, const char *reason); +/* Halts script execution and informs the user about an internal kernel error or failed assertion +** Paramters: (state_t *) s: The state to use +** (const char *) file: The file the oops occured in +** (int) line: The line the oops occured in +** (const char *) reason: Reason for the kernel oops +*/ + + + + +/******************** Priority macros/functions ********************/ + +struct _state; + +extern int sci01_priority_table_flags; /* 1: delete, 2: print */ + +int +_find_priority_band(struct _state *s, int band); +/* Finds the position of the priority band specified +** Parameters: (state_t *) s: State to search in +** (int) band: Band to look for +** Returns : (int) Offset at which the band starts +*/ + +int +_find_view_priority(struct _state *s, int y); +/* Does the opposite of _find_priority_band +** Parameters: (state_t *) s: State +** (int) y: Coordinate to check +** Returns : (int) The priority band y belongs to +*/ + +#define SCI0_VIEW_PRIORITY_14_ZONES(y) (((y) < s->priority_first)? 0 : (((y) >= s->priority_last)? 14 : 1\ + + ((((y) - s->priority_first) * 14) / (s->priority_last - s->priority_first)))) + +#define SCI0_PRIORITY_BAND_FIRST_14_ZONES(nr) ((((nr) == 0)? 0 : \ + ((s->priority_first) + (((nr)-1) * (s->priority_last - s->priority_first)) / 14))) + +#define SCI0_VIEW_PRIORITY(y) (((y) < s->priority_first)? 0 : (((y) >= s->priority_last)? 14 : 1\ + + ((((y) - s->priority_first) * 15) / (s->priority_last - s->priority_first)))) + +#define SCI0_PRIORITY_BAND_FIRST(nr) ((((nr) == 0)? 0 : \ + ((s->priority_first) + (((nr)-1) * (s->priority_last - s->priority_first)) / 15))) + +#define VIEW_PRIORITY(y) _find_view_priority(s, y) +#define PRIORITY_BAND_FIRST(nr) _find_priority_band(s, nr) + + + + + +/******************** Dynamic view list functions ********************/ + +abs_rect_t +set_base(struct _state *s, reg_t object); +/* Determines the base rectangle of the specified view object +** Parameters: (state_t *) s: The state to use +** (reg_t) object: The object to set +** Returns : (abs_rect) The absolute base rectangle +*/ + +extern abs_rect_t +get_nsrect(struct _state *s, reg_t object, byte clip); +/* Determines the now-seen rectangle of a view object +** Parameters: (state_t *) s: The state to use +** (reg_t) object: The object to check +** (byte) clip: Flag to determine wheter priority band +** clipping should be performed +** Returns : (abs_rect) The absolute rectangle describing the +** now-seen area. +*/ + +void +_k_dyn_view_list_prepare_change(struct _state *s); + /* Removes all views in anticipation of a new window or text */ +void +_k_dyn_view_list_accept_change(struct _state *s); + /* Redraws all views after a new window or text was added */ + + + + +/******************** Misc functions ********************/ + +void +process_sound_events(struct _state *s); /* Get all sound events, apply their changes to the heap */ + +#define LOOKUP_NODE(addr) lookup_node(s, (addr), __FILE__, __LINE__) +#define LOOKUP_LIST(addr) lookup_list(s, addr, __FILE__, __LINE__) + +node_t * +lookup_node(struct _state *s, reg_t addr, const char *file, int line); +/* Resolves an address into a list node +** Parameters: (state_t *) s: The state to operate on +** (reg_t) addr: The address to resolve +** (const char *) file: The file the function was called from +** (int) line: The line number the function was called from +** Returns : (node_t *) The list node referenced, or NULL on error +*/ + + +list_t * +lookup_list(struct _state *s, reg_t addr, const char *file, int line); +/* Resolves a list pointer to a list +** Parameters: (state_t *) s: The state to operate on +** (reg_t) addr: The address to resolve +** (const char *) file: The file the function was called from +** (int) line: The line number the function was called from +** Returns : (list_t *) The list referenced, or NULL on error +*/ + + + +/******************** Constants ********************/ + +/* Maximum length of a savegame name (including terminator character) */ +#define SCI_MAX_SAVENAME_LENGTH 0x24 + +/* Flags for the signal selector */ +#define _K_VIEW_SIG_FLAG_STOP_UPDATE 0x0001 +#define _K_VIEW_SIG_FLAG_UPDATED 0x0002 +#define _K_VIEW_SIG_FLAG_NO_UPDATE 0x0004 +#define _K_VIEW_SIG_FLAG_HIDDEN 0x0008 +#define _K_VIEW_SIG_FLAG_FIX_PRI_ON 0x0010 +#define _K_VIEW_SIG_FLAG_ALWAYS_UPDATE 0x0020 +#define _K_VIEW_SIG_FLAG_FORCE_UPDATE 0x0040 +#define _K_VIEW_SIG_FLAG_REMOVE 0x0080 +#define _K_VIEW_SIG_FLAG_FROZEN 0x0100 +#define _K_VIEW_SIG_FLAG_IS_EXTRA 0x0200 +#define _K_VIEW_SIG_FLAG_HIT_OBSTACLE 0x0400 +#define _K_VIEW_SIG_FLAG_DOESNT_TURN 0x0800 +#define _K_VIEW_SIG_FLAG_NO_CYCLER 0x1000 +#define _K_VIEW_SIG_FLAG_IGNORE_HORIZON 0x2000 +#define _K_VIEW_SIG_FLAG_IGNORE_ACTOR 0x4000 +#define _K_VIEW_SIG_FLAG_DISPOSE_ME 0x8000 + +#define _K_VIEW_SIG_FLAG_FREESCI_STOPUPD 0x20000000 /* View has been stop-updated */ + + +/* Sound status */ +#define _K_SOUND_STATUS_STOPPED 0 +#define _K_SOUND_STATUS_INITIALIZED 1 +#define _K_SOUND_STATUS_PAUSED 2 +#define _K_SOUND_STATUS_PLAYING 3 + + + +/* Kernel optimization flags */ +#define KERNEL_OPT_FLAG_GOT_EVENT (1<<0) +#define KERNEL_OPT_FLAG_GOT_2NDEVENT (1<<1) + + +/******************** Kernel functions ********************/ + +/* Generic description: */ +typedef reg_t kfunct(struct _state *s, int funct_nr, int argc, reg_t *argv); + +#define FREESCI_KFUNCT_GLUTTON 1 + +typedef struct { + kfunct *fun; /* The actual function */ + const char *signature; /* kfunct signature */ + const char *orig_name; /* Original name, in case we couldn't map it */ +} kfunct_sig_pair_t; + +#define KF_OLD 0 +#define KF_NEW 1 +#define KF_NONE -1 /* No mapping, but name is known */ +#define KF_TERMINATOR -42 /* terminates kfunct_mappers */ + +typedef struct { + int type; /* KF_* */ + const char *name; + kfunct_sig_pair_t new; +} sci_kernel_function_t; + +extern sci_kernel_function_t kfunct_mappers[]; + +#endif /* _SCI_KERNEL_H_ */ diff --git a/engines/sci/include/list.h b/engines/sci/include/list.h new file mode 100644 index 0000000000..d53b87f491 --- /dev/null +++ b/engines/sci/include/list.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +/* 2006-04-21 Modified by Walter van Niftrik. */ + +#include + +#ifndef _SCI_LIST_H +#define _SCI_LIST_H + +/* List definitions. */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +#define LIST_INIT(head) do { \ + (head)->lh_first = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ +} while (0) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = ((head)->lh_first); \ + (var); \ + (var) = ((var)->field.le_next)) + +/* List access methods. */ +#define LIST_EMPTY(head) ((head)->lh_first == NULL) +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +/* Circular list definitions. */ + +#define CLIST_HEAD(name, type) \ +struct name { \ + struct type *clh_first; /* first element. */ \ +} + +#define CLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define CLIST_ENTRY(type) \ +struct { \ + struct type *cle_next; /* next element. */ \ + struct type *cle_prev; /* previous element */ \ +} + +#define CLIST_INIT(head) do { \ + (head)->clh_first = NULL; \ +} while (0) + +#define CLIST_INSERT_HEAD(head, elm, field) do { \ + if ((head)->clh_first == NULL) \ + (elm)->field.cle_next = (elm)->field.cle_prev = (elm); \ + else { \ + (elm)->field.cle_next = (head)->clh_first; \ + (elm)->field.cle_prev = \ + (head)->clh_first->field.cle_prev; \ + (head)->clh_first->field.cle_prev = (elm); \ + (elm)->field.cle_prev->field.cle_next = (elm); \ + } \ + (head)->clh_first = (elm); \ +} while (0) + +#define CLIST_INSERT_AFTER(listelm, elm, field) do { \ + (elm)->field.cle_prev = (listelm); \ + (elm)->field.cle_next = (listelm)->field.cle_next; \ + (listelm)->field.cle_next->field.cle_prev = (elm); \ + (listelm)->field.cle_next = (elm); \ +} while (0) + +#define CLIST_REMOVE(head, elm, field) do { \ + if ((elm)->field.cle_next == (elm)) \ + (head)->clh_first = NULL; \ + else { \ + if ((head)->clh_first == (elm)) \ + (head)->clh_first = (elm)->field.cle_next; \ + (elm)->field.cle_prev->field.cle_next = \ + (elm)->field.cle_next; \ + (elm)->field.cle_next->field.cle_prev = \ + (elm)->field.cle_prev; \ + } \ +} while (0) + +#define CLIST_FOREACH(var, head, field) \ + for ((var) = (head)->clh_first; \ + (var); \ + (var) = ((var)->field.cle_next == (head)->clh_first ? \ + NULL : (var)->field.cle_next)) + +/* Circular list access methods. */ +#define CLIST_EMPTY(head) ((head)->clh_first == NULL) +#define CLIST_FIRST(head) ((head)->clh_first) +#define CLIST_NEXT(elm, field) ((elm)->field.cle_next) +#define CLIST_PREV(elm, field) ((elm)->field.cle_prev) + +#endif /* !_SCI_LIST_H */ diff --git a/engines/sci/include/listener.h b/engines/sci/include/listener.h new file mode 100644 index 0000000000..5e01696c30 --- /dev/null +++ b/engines/sci/include/listener.h @@ -0,0 +1,38 @@ +/*************************************************************************** + listener.h Copyright (C) 2003 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef _LISTENER_H_ +# define _LISTENER_H_ + +/* Event listener interface */ + +typedef struct { + void (*notify)(void *self, void *notifier); + void *self; +} listener_t; + +#endif diff --git a/engines/sci/include/menubar.h b/engines/sci/include/menubar.h new file mode 100644 index 0000000000..b66b43230a --- /dev/null +++ b/engines/sci/include/menubar.h @@ -0,0 +1,219 @@ +/*************************************************************************** + menubar.h Copyright (C) 1999,2000,01 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ +/* Header for SCI0 menu bar management */ + +#ifndef _SCI_MENUBAR_H_ +#define _SCI_MENUBAR_H_ + +#include +#include + +struct _state; + +#define MENU_FREESCI_BLATANT_PLUG 0xfff0 +/* This adds an "About FreeSCI" menu option to the first menu */ + + +#define MENU_HBAR_STRING_1 "--!" +#define MENU_HBAR_STRING_2 "-!" +#define MENU_HBAR_STRING_3 "!--" +/* These strings are used in SCI to determine an empty menu line */ + +#define MENU_BORDER_SIZE 0 +/* The number of pixels added to the left and right to the text of a menu on the menu bar */ + +#define MENU_LEFT_BORDER 5 +/* The number of pixels added to the left of the first menu */ + +#define MENU_BOX_CENTER_PADDING 10 +/* Number of pixels to leave in between the left and the right centered text content in boxes +** that use right centered content +*/ + +#define MENU_BOX_LEFT_PADDING 0 +/* Number of pixels to pad to the left */ +#define MENU_BOX_RIGHT_PADDING 2 +/* Number of pixels to pad to the right */ + +#define MENU_BAR_HEIGHT 10 + + +#define MENU_TYPE_NORMAL 0 +#define MENU_TYPE_HBAR 1 /* Horizontal bar */ + +/* Special characters used while building the menu bar */ +#define SCI_SPECIAL_CHAR_FUNCTION 'F' +#define SCI_SPECIAL_CHAR_CTRL 3 +#define SCI_SPECIAL_CHAR_ALT 2 + +/* Maximum number of bytes per SAID spec */ +#define MENU_SAID_SPEC_SIZE 64 + +#define MENU_ATTRIBUTE_SAID 0x6d +#define MENU_ATTRIBUTE_TEXT 0x6e +#define MENU_ATTRIBUTE_KEY 0x6f +#define MENU_ATTRIBUTE_ENABLED 0x70 +#define MENU_ATTRIBUTE_TAG 0x71 + +/* Those flags determine whether the corresponding menu_item_t entries are valid */ +#define MENU_ATTRIBUTE_FLAGS_KEY 0x01 +#define MENU_ATTRIBUTE_FLAGS_SAID 0x02 + +typedef struct { + int type; /* Normal or hbar */ + char *keytext; /* right-centered part of the text (the key) */ + int keytext_size; /* Width of the right-centered text */ + + int flags; + byte said[MENU_SAID_SPEC_SIZE]; /* Said spec for this item */ + reg_t said_pos; + char *text; + reg_t text_pos; + int modifiers, key; /* Hotkey for this item */ + int enabled; + int tag; + +} menu_item_t; + + +typedef struct { + char *title; + + int title_width; /* Width of the title in pixels */ + int width; /* Pixel width of the menu window */ + + int items_nr; /* Window height equals to intems_nr * 10 */ + menu_item_t *items; /* Actual entries into the menu */ + +} menu_t; + + + +typedef struct { + + int menus_nr; + menu_t *menus; /* The actual menus */ + +} menubar_t; + +struct gfx_port; +struct gfx_picture; /* forward declarations for graphics.h */ + + +/********** function definitions *********/ + +menubar_t * +menubar_new(void); +/* Creates a new menubar struct +** Parameters: (void) +** Returns : (menubar_t *) A pointer to the new menubar entity +** To free the entity, call menubar_free. +*/ + + +void +menubar_free(menubar_t *menubar); +/* Frees all memory associated with a menubar +** Parameters: (menubar_t *) menubar: The menubar to free +** Returns : (void) +*/ + + +void +menubar_add_menu(gfx_state_t *state, menubar_t *menubar, char *title, char *entries, int font, reg_t entries_base); +/* Adds a menu to the menubar. +** Parameters: (gfx_state_t *) state: The state the fonts are stored in +** (menubar_t *) menubar: The menubar to operate on +** (char *) title: The menu title +** (char *) entries: A string of menu entries +** (int) font: The font which is to be used for drawing +** (reg_t) entries_base: Segmented VM address of the entries string +** Returns : (void) +** The menu entries use the following special characters: +** '`' : Right justify the following part +** ':' : End of this entry +** '#' : Function key (replaced by 'F') +** '^' : Control key (replaced by \002, which looks like "CTRL") +** '=' : Initial tag value +** and the special string "--!", which represents a horizontal bar in the menu. +** +** If MENU_FREESCI_BLATANT_PLUG is defined, an additional option "About FreeSCI" will be +** added if this was the first menu to be added to the menu bar. +*/ + + +int +menubar_set_attribute(struct _state *s, int menu, int item, int attribute, reg_t value); +/* Sets the (currently unidentified) foo and bar values. +** Parameters: (state_t *) s: The current state +** (int) menu: The menu number to edit +** (int) item: The menu item to change +** (int) attribute: The attribute to modify +** (int) value: The value the attribute should be set to +** Returns : (int) 0 on success, 1 if either menu or item were invalid +*/ + + +reg_t +menubar_get_attribute(struct _state *s, int menu, int item, int attribute); +/* Sets the (currently unidentified) foo and bar values. +** Parameters: (state_t *) s: The current state +** (int) menu: The menu number +** (int) item: The menu item to read +** (int) attribute: The attribute to read from +** Returns : (int) The attribute value, or -1 on error +*/ + + +int +menubar_item_valid(struct _state *s, int menu, int item); +/* Determines whether the specified menu entry may be activated +** Parameters: (state_t *) s: The current state +** (int x int) (menu, item): The menu item to check +** Returns : (int) 1 if the menu item may be selected, 0 otherwise +*/ + + +int +menubar_map_pointer(struct _state *s, int *menu_nr, int *item_nr, gfxw_port_t *port); +/* Maps the pointer position to a (menu,item) tuple. +** Parameters: (state_t *) s: The current state +** ((int *) x (int *)) (menu_nr, item_nr): Pointers to the current menu/item tuple +** (port_t *) port: The port of the currently active menu (if any) +** Returns : (int) 1 if the pointer is outside a valid port, 0 otherwise. +*/ + +int +menubar_match_key(menu_item_t *item, int message, int modifiers); +/* Determines whether a message/modifiers key pair matches a menu item's key parameters +** Parameters: (menu_item_t *) item: The menu item to match +** (int x int) message, modifiers: The input to compare +** Returns : (int) 1 on match, 0 otherwise +*/ + +#endif /* !_SCI_MENUBAR_H_ */ + diff --git a/engines/sci/include/modules.h b/engines/sci/include/modules.h new file mode 100644 index 0000000000..9a64956326 --- /dev/null +++ b/engines/sci/include/modules.h @@ -0,0 +1,84 @@ +/*************************************************************************** + modules.h Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef _FREESCI_MODULES_H_ +#define _FREESCI_MODULES_H_ + +#include + +#ifdef _WIN32 +# define MODULE_NAME_SUFFIX ".dll" +#else +# define MODULE_NAME_SUFFIX ".so" +#endif + + +/* Modules must sub-type this structure. Oh, wait, C doesn't do sub-typing. +** Well, they have to start the same, anyway... */ +typedef struct { + char *module_name; /* Module name */ + char *module_version; /* Module version */ + int class_magic; /* Magic ID to identify drivers of certain types */ + int class_version; /* Version of that particular driver class */ + + /* More entries might be added later, but won't be checked if magic or + ** version don't match. */ + +} sci_module_t; + + +void * +sci_find_module(char *path, char *name, char *type, char *struct_prefix, + char *file_suffix, int magic, int version, void **handle); +/* Attempts to open a module with the specified parameters in the path +** Parameters: (char *) path: The path to look in; supports several directories +** (char *) name: Module name +** (char *) type: Module type string (see below) +** (char *) struct_prefix: see below +** (char *) file_suffix: see below +** (int) magic: Magic number to identify the module type +** (int) version: The only version to support +** (void **) handle: Pointer to a void * which a handle is written +** to (for use with sci_close_module()). +** Returns : (void *) NULL if no module was found, a pointer to the structure +** otherwise +** This function looks for the structure 'struct_prefix + name' in a file called +** 'name + file_suffix + MODULE_NAME_SUFFIX' in the 'type' subdirectory. +** It only success if both the magic and the version number match. +*/ + +void +sci_close_module(void *module, char *type, char *name); +/* Closes a previously found module +** Parameters: (void *) module: Reference to the module to close +** (char *) type: Type of the module (for debugging) +** (char *) name: Module name (for debugging) +** Returns : (void) +*/ + + +#endif /* !_FREESCI_MODULES_H_ */ diff --git a/engines/sci/include/old_objects.h b/engines/sci/include/old_objects.h new file mode 100644 index 0000000000..850e489a83 --- /dev/null +++ b/engines/sci/include/old_objects.h @@ -0,0 +1,54 @@ +#ifndef OLD_OBJECTS_H +#define OLD_OBJECTS_H + +#include +#include + +#ifdef __cplusplus +# define new new_ +# define delete delete_ +# define class class_ +#endif /* __cplusplus */ + +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* class; + 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/engines/sci/include/reg_t_hashmap.h b/engines/sci/include/reg_t_hashmap.h new file mode 100644 index 0000000000..0bd89fe8f2 --- /dev/null +++ b/engines/sci/include/reg_t_hashmap.h @@ -0,0 +1,71 @@ +/*************************************************************************** + int_hashmap.h Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef _REG_T_HASHMAP_H_ +#define _REG_T_HASHMAP_H_ + +#define DCS_REGT_HASH_MAX 512 + +#include "vm.h" +#ifdef HASH_MAX +# undef HASH_MAX +# undef COMP +# undef HASH +#endif + +#define HASH_MAX DCS_REGT_HASH_MAX +#define COMP(x, y) compare_reg_t(x, y) +#define HASH(x) (((x.segment << 3) | x.offset) & 0x1ff) +#undef MUST_FREE + +#include "hashmap.h" +DECLARE_STRUCTS(reg_t) +DECLARE_FUNCTIONS(reg_t) + +#ifndef BUILD_MAP_FUNCTIONS +# undef HASH_MAX +# undef COMP +# undef HASH +#endif + +/* see hashmap.h for descriptions of these functions */ +reg_t_hash_map_ptr +new_reg_t_hash_map(void); + +void +free_reg_t_hash_map(reg_t_hash_map_ptr); + +int +reg_t_hash_map_check_value(reg_t_hash_map_ptr, reg_t value, char add_p, char *added); + +int +reg_t_hash_map_remove_value(reg_t_hash_map_ptr, reg_t value); + +void +apply_to_reg_t_hash_map(reg_t_hash_map_ptr map, void *param, void (*note) (void *param, reg_t name, int value)); + +#endif /* _INT_HASHMAP_H_ */ diff --git a/engines/sci/include/resource.h b/engines/sci/include/resource.h new file mode 100644 index 0000000000..cbb333f574 --- /dev/null +++ b/engines/sci/include/resource.h @@ -0,0 +1,510 @@ +/*************************************************************************** + resource.h Copyright (C) 1999,2000,01,02 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) [creichen@rbg.informatik.tu-darmstadt.de] + + History: + + 990327 - created (CR) + +***************************************************************************/ + +#ifndef FREESCI_PRIMARY_RESOURCE_H_ +#define FREESCI_PRIMARY_RESOURCE_H_ + +/** This header file defines (mostly) generic tools and utility functions. + ** It also handles portability stuff, in cooperation with scitypes.h + ** (which specializes in primitive data types). + ** Most implementations of the functions found here are in + ** $(SRCDIR)/src/scicore/tools.c + ** + ** -- Christoph Reichenbach + **/ + +#define SCI_INVALID_FD -1 +#define IS_VALID_FD(a) ((a) != SCI_INVALID_FD) /* Tests validity of a file descriptor */ + +#ifdef _WIN32 +# ifndef _Win32 +# define _Win32 +/* Work around problem with some versions of flex */ +# endif +#endif + +/*#define _SCI_RESOURCE_DEBUG */ +/*#define _SCI_DECOMPRESS_DEBUG*/ +#ifndef WITH_DMALLOC +# define SCI_SAFE_ALLOC /* Undefine for debugging */ +#endif + +#ifdef SCUMMVM +//TODO: Remove these defines by replacing their functionality by their ScummVM counterparts +#define HAVE_ISBLANK +#define HAVE_UNISTD_H +#define HAVE_FCNTL_H +#define HAVE_UNLINK +#define HAVE_RMDIR +#define HAVE_MEMCHR +#define HAVE_FNMATCH_H +#define HAVE_SYS_TIME_H +#define HAVE_GETTIMEOFDAY +#define VERSION "0.6.4" +#endif // SCUMMVM + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifndef __unix__ +# if defined(__OpenBSD__) || defined(__NetBSD__) +# define __unix__ +# endif +#endif + +#if defined(HAVE_FORK) && !defined(__BEOS__) +# define HAVE_SYSV_IPC +#else +# undef HAVE_SYSV_IPC +#endif + +#ifdef __DECC +# include +#endif +#include +#include +#include +#include + +#ifdef WITH_DMALLOC +# include +#endif + +#include + +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifdef _WIN32 +# include +# include +#else /* !_WIN32 */ +# define DLLEXTERN +#endif /* !_WIN32 */ + +#ifdef __BEOS__ +# include +# define usleep snooze +#endif + +#ifdef _MSC_VER +# include +# include +# include +#endif + + +#ifdef HAVE_FCNTL_H +# include +#endif +#include +#include +#ifdef HAVE_SYS_STAT_H +# include +#endif +#include +#ifdef _DOS +# include +#endif +#ifdef HAVE_LIMITS_H +# include +#endif + + +#ifdef _MSC_VER +# ifdef FREESCI_EXPORTS +# define DLLEXTERN +# else +# define DLLEXTERN __declspec(dllimport) +#endif +#else +# define DLLEXTERN +#endif + +#if _MSC_VER || _DOS +# define G_DIR_SEPARATOR_S "\\" +# define G_DIR_SEPARATOR '\\' +#else +# define G_DIR_SEPARATOR_S "/" +# define G_DIR_SEPARATOR '/' +#endif + +#if defined(__MORPHOS__) || defined(_MSC_VER) || defined(ARM_WINCE) || defined(__amigaos4__) +# define PATH_MAX 255 +#endif + +#ifndef MIN +# define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef MAX +# define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif /* HP-UX defines both */ + + +#define GUINT16_SWAP_LE_BE_CONSTANT(val) ((((val) & 0x00ff) << 8) | (((val) & 0xff00) >> 8)) + +#define GUINT32_SWAP_LE_BE_CONSTANT(val) ( \ + (((val) & 0xff000000) >> 24) \ + | (((val) & 0x00ff0000) >> 8) \ + | (((val) & 0x0000ff00) << 8) \ + | (((val) & 0x000000ff) << 24)) + +#define SCI_MAX_RESOURCE_SIZE 0x0400000 +/* The maximum allowed size for a compressed or decompressed resource */ + + + +#define MAX_HOMEDIR_SIZE 255 + +#ifdef _WIN32 +# define FO_BINARY "b" +#else +# define FO_BINARY "" +#endif + +#ifdef _WIN32 +# define FO_TEXT "t" +#else +# define FO_TEXT "" +#endif + +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +/**** FUNCTION DECLARATIONS ****/ + +#ifdef _WIN32 +# define scimkdir(arg1,arg2) mkdir(arg1) +#else +# define scimkdir(arg1,arg2) mkdir(arg1,arg2) +#endif + +#ifndef _GET_INT_16 +#define _GET_INT_16 + +static inline gint16 +getInt16(byte *d) +{ + return (gint16)(*d | (d[1] << 8)); +} + +#define getUInt16(d) (guint16)(getInt16(d)) +#endif +/* Turns a little endian 16 bit value into a machine-dependant 16 bit value +** Parameters: d: Pointer to the memory position from which to read +** Returns : (gint16) The (possibly converted) 16 bit value +** getUInt16 returns the int unsigned. +*/ + +static inline void +putInt16(byte* dest, int src) +{ + dest[0]=(byte)src&0xff; + dest[1]=(byte)(src>>8)&0xff; +} +/* Converse of getInt16() +** Parameters: (byte *) dest: The position to write to +** (int) src: value to write +*/ + +#ifndef HAVE_ISBLANK +static inline int +isblank(int foo) +{ + return (foo == ' ') || (foo == '\t'); +} +#endif + +#ifdef _cplusplus +# define delete _freesci_cplusplus_workaround_delete +# define new _freesci_cplusplus_workaround_new +#endif + +#define SCI_MEMTEST memtest(__FILE__, __LINE__) + +/*-- queues --*/ + +typedef struct _sci_queue_node { + void *data; + int type; + struct _sci_queue_node *next; +} sci_queue_node_t; + +typedef struct { + sci_queue_node_t *start; /* Where things go in */ + sci_queue_node_t *end; /* Where they come out */ +} sci_queue_t; + +sci_queue_t * +sci_init_queue(sci_queue_t *queue); +/* Initializes an SCI queue +** Parameters: (sci_queue_t *) queue: Pointer to the queue to initialize +** Returns : (sci_queue_t *) queue +*/ + +sci_queue_t * +sci_add_to_queue(sci_queue_t *queue, void *data, int type); +/* Adds an entry to an initialized SCI queue +** Parameters: (sci_queue_t *) queue: The queue to add to +** (void *) data: The data to contain +** (int) type: Some metadata, e.g. size of the data block +** Returns : (sci_queue_t *) queue +** The meaning of the 'type' field may be determined individually. +*/ + +void * +sci_get_from_queue(sci_queue_t *queue, int *type); +/* Reads an entry from an SCI queue +** Parameters: (sci_queue_t *) queue: The queue to remove from +** (int *) type: A pointer to a variable to write the metadata +** into, or NULL to omit this step +** Returns : (void *) The data block contained, or NULL if none was inside +*/ + +/* --- */ + +int +memtest(const char *file, int line); +/* Allocates, manipulates, and frees some memory +** Parameters: (const char *) file: The file name to print when starting the +** tests +** (int) line: The line number to claim it was executed on +** Returns : (int) 0 +** This function calls malloc(), free(), and memfrob() or memset() +** to provocate segmentation faults caused by dynamic allocation bugs +** in previous parts of the code. +*/ + +void +sci_gettime(long *seconds, long *useconds); +/* Calculates the current time in seconds and microseconds +** Parameters: (long *) seconds: Pointer to the variable the seconds part of the +** current time will be stored in +** (long *) useconds: Pointer to the variable the microseconds part +** of the current time will be stored in +** Returns : (void) +** The resulting values must be relative to an arbitrary fixed point in time +** (typically 01/01/1970 on *NIX systems). +*/ + +void +sci_get_current_time(GTimeVal *val); +/* GTimeVal version of sci_gettime() +** Parameters: (GTimeVal *) val: Pointer to the structure the values will be stored in +** Returns : (void) +*/ + +void +sci_init_dir(sci_dir_t *dirent); +/* Initializes an sci directory search structure +** Parameters: (sci_dir_t *) dirent: The entity to initialize +** Returns : (void) +** The entity is initialized to "empty" values, meaning that it can be +** used in subsequent sci_find_first/sci_find_next constructs. In no +** event should this function be used upon a structure which has been +** subjected to any of the other dirent calls. +*/ + +char * +sci_find_first(sci_dir_t *dirent, const char *mask); +/* Finds the first file matching the specified file mask +** Parameters: (sci_dir_t *) dirent: Pointer to an unused dirent structure +** (const char *) mask: File mask to apply +** Returns : (char *) Name of the first matching file found, or NULL +*/ + +char * +sci_find_next(sci_dir_t *dirent); +/* Finds the next file specified by an sci_dir initialized by sci_find_first() +** Parameters: (sci_dir_t *) dirent: Pointer to SCI dir entity +** Returns : (char *) Name of the next matching file, or NULL +*/ + +void +sci_finish_find(sci_dir_t *dirent); +/* Completes an 'sci_find_first/next' procedure +** Parameters: (sci_dir_t *) dirent: Pointer to the dirent used +** Returns : (void) +** In the operation sequences +** sci_init_dir(x); sci_finish_find(x); +** and +** sci_finish_find(x); sci_finish_find(x); +** the second operation is guaranteed to be a no-op. +*/ + +FILE * +sci_fopen(const char *fname, const char *mode); +/* Opens a FILE* case-insensitively +** Parameters: (const char *) fname: Name of the file to open +** (const char *) mode: Mode to open it with +** Returns : (FILE *) A valid file handle, or NULL on failure +** Always refers to the cwd, cannot address subdirectories +*/ + +int +sci_open(const char *fname, int flags); +/* Opens a file descriptor case-insensitively +** Parameters: (const char *) fname: Name of the file to open +** (int) flags: open(2) flags for the file +** Returns : (int) a file descriptor of the open file, +** or SCI_INVALID_FD on failure +** Always refers to the cwd, cannot address subdirectories +*/ + + +int +sciprintf(const char *fmt, ...); +#define gfxprintf sciprintf +/* Prints a string to the console stack +** Parameters: fmt: a printf-style format string +** ...: Additional parameters as defined in fmt +** Returns : (int) 1 +** Implementation is in src/scicore/console.c +*/ + +char * +sci_getcwd(void); +/* Returns the current working directory, malloc'd. +** Parameters: (void) +** Returns : (char *) a malloc'd cwd, or NULL if it couldn't be determined. +*/ + + +char * +sci_get_homedir(void); +/* Returns the user's home directory +** Parameters: (void) +** Returns : (char *) Pointer to a static buffer containing the user's home, +** or NULL if there is no such thing. +*/ + +int +sci_mkpath(const char *path); +/* Asserts that the specified path is available +** Parameters: (const char *) path: Path to verify/create +** Returns : (int) 0 on success, <0 on error +** This function will create any directories that couldn't be found +*/ + +int +sci_fd_size(int fd); +/* Returns the filesize of an open file +** Parameters: (int) fd: File descriptor of open file +** Returns : (int) filesize of file pointed to by fd, -1 on error +*/ + +int +sci_file_size(const char *fname); +/* Returns the filesize of a file +** Parameters: (const char *) fname: Name of file to get filesize of +** Returns : (int) filesize of the file, -1 on error +*/ + +char * +_fcaseseek(const char *fname, sci_dir_t *dir); +/* Returns the case-sensitive filename of a file. +** Expects *dir to be uninitialized and the caller to free it afterwards. +** Parameters: (const char *) fname: Name of file to get case-sensitive. +** (sci_dir_t *) dir: Directory to find file within. +** Returns : (char *) Case-sensitive filename of the file. +*/ + +/* Simple heuristic to work around array handling peculiarity in SQ4: +It uses StrAt() to read the individual elements, so we must determine +whether a string is really a string or an array. */ +int is_print_str(char *str); + +#ifdef HAVE_UNLINK +# define sci_unlink unlink +#else /* !HAVE_UNLINK */ +# error "Please provide an int sci_unlink(const char *) for removing filesystem entries" +#endif /* !HAVE_UNLINK */ + +#ifdef HAVE_RMDIR +# define sci_rmdir rmdir +#else /* !HAVE_RMDIR */ +# ifdef WIN32 +# define sci_rmdir _rmdir +# else +# error "Please provide an int sci_rmdir(const char *) for removing directories" +# endif +#endif /* !HAVE_RMDIR */ + +#ifndef HAVE_FFS +int sci_ffs(int _mask); +#else +#define sci_ffs ffs +#endif + + +void +sci_sched_yield(void); +/* Yields the running process/thread to the scheduler +** Parameters: (void) +** Returns : after a while. +*/ + +/* The following was originally based on glib.h code, which was + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + */ +#if defined (__GNUC__) && __GNUC__ >= 2 +# if defined (__i386__) +# define BREAKPOINT() {__asm__ __volatile__ ("int $03"); } +# elif defined(__alpha__) +# define BREAKPOINT() {__asm__ __volatile__ ("call_pal 0x80"); } +# endif /* !__i386__ && !__alpha__ */ +#elif defined (_MSC_VER) +# if defined (_M_IX86) +# define BREAKPOINT() { __asm { int 03 } } +# elif defined(_M_ALPHA) +# define BREAKPOINT() { __asm { bpt } } +# endif /* !_M_IX86 && !_M_ALPHA */ +#elif defined (__DECC) +# if defined(__alpha__) +# define BREAKPOINT() {asm ("call_pal 0x80"); } +# endif /* !__i386__ && !__alpha__ */ +#endif +#ifndef BREAKPOINT +# define BREAKPOINT() { fprintf(stderr, "Missed breakpoint in %s, line %d\n", __FILE__, __LINE__); *((int *) NULL) = 42; } +#endif /* !BREAKPOINT() */ + +#define WARNING(foo) {char i; i = 500;} + +#endif + + + + + + diff --git a/engines/sci/include/sbtree.h b/engines/sci/include/sbtree.h new file mode 100644 index 0000000000..38f8a71ba1 --- /dev/null +++ b/engines/sci/include/sbtree.h @@ -0,0 +1,114 @@ +/*************************************************************************** + sbtree.h Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Static binary tree header file */ + +/* Static binary trees are used to link ints to pointers. They are not capable +** of resizing after being initialized. +*/ + +#ifndef _SBTREE_H_ +#define _SBTREE_H_ + +#ifdef SBTREE_DEBUG +# include +#endif + + +typedef struct { + int entries_nr; + int min_entry; + int max_entry; + int levels; + int alloced_entries; + void *data; +} sbtree_t; + + +sbtree_t * +sbtree_new(int size, int *keys); +/* Generates a new sbtree with the specified key set +** Parameters: (int) size: Number of entries in 'keys' +** (int *) keys: Array of 'size' integer keys +** Returns : (sbtree_t *) A new sbtree +** Only positive keys are allowed. Duplicates are not detected and will +** cause additional memory usage and slower access times if not removed +** beforehand. +*/ + +void +sbtree_free(sbtree_t *tree); +/* Frees an sbtree +** Parameters: (sbtree_t *) tree: The tree to free +** Returns : (void) Nothing at all +*/ + +int +sbtree_set(sbtree_t *tree, int key, void *value); +/* Sets a key to a value +** Parameters: (sbtree_t *) tree: The tree to modify +** (int) key: The key to set +** (void *) value: The value part of the (key,value) tuple +** Returns : (int) 0 if everything went OK, -1 if the key was invalid +** value may, of course, be NULL here. +*/ + +void * +sbtree_get(sbtree_t *tree, int key); +/* Retreives a key +** Parameters: (sbtree_t *) tree: The tree to search in +** (int) key: The key to retreive +** Returns : (void *) The value mapped to the key +** If key was not found/invalid, NULL is returned. Note that there is no +** way of distinguishing between keys mapped to NULL and invalid keys, +** short of attempting to set that key. +*/ + +void +sbtree_foreach(sbtree_t *tree, void *args, void *(*operation)(sbtree_t *, const int, + const void *, void *)); +/* Operates once on each entry in the tree +** Parameters: (sbtree_t *) tree: The tree to operate on +** (void *) arguments: Additional arguments to pass to 'operation' +** (int (int, void *, void *)) operation: The operation to execute +** Returns : (void) +** The parameters of the function that is to be executed are as follows: +** operation (sbtree_t *tree, int key, void *value, void *args) +** Parameters: (sbtree_t *) tree: The tree the operation was executed on +** (int) key: Key of the entry the operation was invoked on +** (void *) value: Value of the entry the operation was invoked on +** (void *) args: The 'args' parameter passed to sbtree_foreach() +** Returns : (void *) The new value of this entry +** This function will only work properly the original data contained no duplicate keys. +*/ + + +#endif /* !_SBTREE_H_ */ + + + + + diff --git a/engines/sci/include/sci_conf.h b/engines/sci/include/sci_conf.h new file mode 100644 index 0000000000..da411226ed --- /dev/null +++ b/engines/sci/include/sci_conf.h @@ -0,0 +1,187 @@ +/*************************************************************************** + sci_conf.h Copyright (C) 1999,2000,01 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ +/* Configuration and setup stuff for FreeSCI */ + +#ifndef _SCI_CONFIG_H_ +#define _SCI_CONFIG_H_ + +#include +#include + +#define FREESCI_DRIVER_SUBSYSTEMS_NR 1 + +#define FREESCI_DRIVER_SUBSYSTEM_GFX 0 +#define FREESCI_DRIVER_SUBSYSTEM_PCM 1 +#define FREESCI_DRIVER_SUBSYSTEM_MIDIOUT 2 + +#ifdef _WIN32 +# define SCI_DEFAULT_MODULE_PATH "." +#else +# define SCI_DEFAULT_MODULE_PATH "/usr/local/lib/freesci/:/usr/lib/freesci/" +#endif + +typedef struct _driver_option { + char *option; + char *value; + struct _driver_option *next; +} driver_option_t; + +typedef struct _subsystem_options { + char *name; /* Driver name */ + driver_option_t *options; + struct _subsystem_options *next; /* next driver */ +} subsystem_options_t; + +typedef struct { + /* IMPORTANT: these values correspond directly to what's specified in + ** config.l. Where an option is of type OPTION_TYPE_NVP or + ** OPTION_TYPE_INVERSE_NVP, it is cast as an _integer_. Therefore it + ** should not be any other type except for int here. + */ + + char *name; /* Game identifier */ + sci_version_t version; /* The version to emulate */ + + gfx_options_t gfx_options; + + subsystem_options_t *driver_options[FREESCI_DRIVER_SUBSYSTEMS_NR]; + + int x_scale, y_scale, scale, color_depth; /* GFX subsystem initialization values */ + + int animation_delay; /* Number of microseconds to wait between each pic transition animation cycle */ + int animation_granularity; /* Granularity for pic transition animations */ + int alpha_threshold; /* Crossblitting alpha threshold */ + int unknown_count; /* The number of "unknown" kernel functions */ + char *resource_dir; /* Resource directory */ + char *gfx_driver_name; /* The graphics driver to use */ + char *console_log; /* The file to which console output should be echoed */ + char *menu_dir; /* Directory where the game menu searches for games */ + char debug_mode [80]; /* Characters specifying areas for which debug output should be enabled */ + int mouse; /* Whether the mouse should be active */ + int reverse_stereo; + int res_version; + +#ifdef __GNUC__ +# warning "Re-enable sound stuff" +#endif +#if 0 + midiout_driver_t *midiout_driver; /* the midiout method to use */ + midi_device_t *midi_device; /* the midi device to use */ + + pcmout_driver_t *pcmout_driver; /* the pcm driver to use */ + sound_server_t *sound_server; /* The sound server */ +#endif + guint16 pcmout_rate; /* Sample rate */ + guint8 pcmout_stereo; /* Stereo? */ + + char *module_path; /* path to directories modules are loaded from */ + void *dummy; /* This is sad... */ +} config_entry_t; + +int +config_init(config_entry_t **conf, char *conffil); +/* Initializes the config entry structurre based on information found in the config file. +** Parameters: (config_entry_t **) conf: See below +** (char *) conffile: Filename of the config file, or NULL to use the default name +** Returns : (int) The number of config file entries found +** This function reads the ~/.freesci/config file, parses it, and inserts the appropriate +** data into *conf. *conf will be malloc'd to be an array containing default information in [0] +** and game-specific data in each of the subsequent record entries. +** Not threadsafe. Uses flex-generated code. +*/ + +void +config_free(config_entry_t **conf, int entries); +/* Frees a config entry structure +** Parameters: (config_entry_t **) conf: Pointer to the pointer to the first entry of the list +** (int) entries: Number of entries to free +** Returns : (void) +*/ + + +void * +parse_gfx_driver(char *driver_name); +/* Parses a string and looks up an appropriate driver structure +** Parameters: (char *) driver_name: Name of the driver to look up +** Returns : (void *) A matching driver, or NULL on failure +*/ + +void * +parse_midiout_driver(char *driver_name); +/* Parses a string and looks up an appropriate driver structure +** Parameters: (char *) driver_name: Name of the driver to look up +** Returns : (void *) A matching driver, or NULL on failure +*/ + +void * +parse_midi_device(char *driver_name); +/* Parses a string and looks up an appropriate driver structure +** Parameters: (char *) driver_name: Name of the driver to look up +** Returns : (void *) A matching driver, or NULL on failure +*/ + +void * +parse_pcmout_driver(char *driver_name); +/* Parses a string and looks up an appropriate driver structure +** Parameters: (char *) driver_name: Name of the driver to look up +** Returns : (void *) A matching driver, or NULL on failure +*/ + +void * +parse_sound_server(char *driver_name); +/* Parses a string and looks up an appropriate driver structure +** Parameters: (char *) driver_name: Name of the driver to look up +** Returns : (void *) A matching sound server, or NULL on failure +*/ + +driver_option_t * +get_driver_options(config_entry_t *config, int subsystem, const char *name); +/* Retreives the driver options for one specific driver in a subsystem +** Parameters: (config_entry_t *) config: The config entry to search in +** (int) subsystem: Any of the FREESCI_DRIVER_SUBSYSTEMs +** (const char *) name: Name of the driver to look for +** Returns : (driver_option_t *) A pointer to the first option in +** a singly-linked list of options, or NULL if none was +** found +*/ + +#if 0 +void * +find_module(char *path, char *module_name, char *module_suffix); +/* Tries to find a module in the specified path +** Parameters: (char *) path: The path to search in (specified in config) +** (char *) module_name: Name of the module to look for +** (char *) module_suffix: Module structure to look for +** More precisely, the module "module_name" + MODULE_NAME_SUFFIX is +** looked for in all members of the path. If it is found, + +** FIXME: First need to add generic module architecture + +*/ +#endif + +#endif /* !_SCI_CONFIG_H */ diff --git a/engines/sci/include/sci_dos.h b/engines/sci/include/sci_dos.h new file mode 100644 index 0000000000..69e8803e29 --- /dev/null +++ b/engines/sci/include/sci_dos.h @@ -0,0 +1,174 @@ +/*************************************************************************** + sci_dos.h Copyright (C) 1999 Rink Springer + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Rink Springer [rink@springer.cx] + +***************************************************************************/ + +#ifndef _SCI_DOS_H_ +#define _SCI_DOS_H_ + +#include + +#ifndef HAVE_DIRENT_H +#define HAVE_DIRENT_H +#endif + +#define TRUE (!FALSE) +#define FALSE (0) + +typedef signed char gint8; +typedef signed short gint16; +typedef signed long gint32; + +typedef unsigned char guint8; +typedef unsigned short guint16; +typedef unsigned long guint32; + +typedef char gchar; +typedef unsigned char guchar; +typedef int gint; +typedef unsigned int guint; +typedef long glong; +typedef unsigned long gulong; + +typedef gint gboolean; + +typedef void* gpointer; + +#define g_new0(type, count) ((type*)g_malloc0((unsigned)sizeof(type) * (count))) +#define g_new(type, count) ((type*)sci_malloc((unsigned)sizeof(type) * (count))) + +#define g_malloc(x) sci_malloc(x) +#define g_free(x) sci_free(x) +#define g_realloc(x,y) sci_realloc(x,y) + +extern gpointer g_malloc0(guint32 size); + +/* Name of package */ +#define PACKAGE "freesci" + +/* Version number of package */ +#define VERSION "0.3.0" + +/* directory separator */ +#define G_DIR_SEPARATOR_S "/" + +/* paths longer than 128 chars? nah... */ +#define PATH_MAX 128 + +#define SSIZE_MAX 1024 + +#define g_get_current_time(x) gettimeofday(x,NULL) + +#define g_strcasecmp(x,y) strcasecmp(x,y) +#define g_strncasecmp(x,y,z) strncasecmp(x,y,z) + +extern gint g_vsnprintf(gchar*,gulong,gchar const*,va_list); +extern gpointer g_memdup (gpointer mem, guint byte_size); + +#define DGFX_KEYUP 72 +#define DGFX_KEYDOWN 80 +#define DGFX_KEYLEFT 75 +#define DGFX_KEYRIGHT 77 +#define DGFX_KEYSTOP 76 /* numberic 5 */ + +#define DGFX_KEY_ESCAPE 1 /* escape */ +#define DGFX_KEY_1 2 /* 1 */ +#define DGFX_KEY_2 3 /* 2 */ +#define DGFX_KEY_3 4 /* 3 */ +#define DGFX_KEY_4 5 /* 4 */ +#define DGFX_KEY_5 6 /* 5 */ +#define DGFX_KEY_6 7 /* 6 */ +#define DGFX_KEY_7 8 /* 7 */ +#define DGFX_KEY_8 9 /* 8 */ +#define DGFX_KEY_9 10 /* 9 */ +#define DGFX_KEY_0 11 /* 0 */ +#define DGFX_KEY_MINUS 12 /* -/_ */ +#define DGFX_KEY_EQUAL 13 /* =/+ */ +#define DGFX_KEY_BSPACE 14 /* backspace */ +#define DGFX_KEY_TAB 15 /* enter */ +#define DGFX_KEY_Q 16 /* q */ +#define DGFX_KEY_W 17 /* w */ +#define DGFX_KEY_E 18 /* e */ +#define DGFX_KEY_R 19 /* r */ +#define DGFX_KEY_T 20 /* t */ +#define DGFX_KEY_Y 21 /* y */ +#define DGFX_KEY_U 22 /* u */ +#define DGFX_KEY_I 23 /* i */ +#define DGFX_KEY_O 24 /* o */ +#define DGFX_KEY_P 25 /* p */ +#define DGFX_KEY_LBRACKET 26 /* [/{ */ +#define DGFX_KEY_RBRACKET 27 /* ]/} */ +#define DGFX_KEY_ENTER 28 /* enter */ +#define DGFX_KEY_CTRL 29 /* control */ +#define DGFX_KEY_A 30 /* a */ +#define DGFX_KEY_S 31 /* s */ +#define DGFX_KEY_D 32 /* d */ +#define DGFX_KEY_F 33 /* f */ +#define DGFX_KEY_G 34 /* g */ +#define DGFX_KEY_H 35 /* h */ +#define DGFX_KEY_J 36 /* j */ +#define DGFX_KEY_K 37 /* k */ +#define DGFX_KEY_L 38 /* l */ +#define DGFX_KEY_COLON 39 /* ;/: */ +#define DGFX_KEY_TILDE 41 /* tilde */ +#define DGFX_KEY_LSHIFT 42 /* left shift */ +#define DGFX_KEY_Z 44 /* z */ +#define DGFX_KEY_X 45 /* x */ +#define DGFX_KEY_C 46 /* c */ +#define DGFX_KEY_V 47 /* v */ +#define DGFX_KEY_B 48 /* b */ +#define DGFX_KEY_N 49 /* n */ +#define DGFX_KEY_M 50 /* m */ +#define DGFX_KEY_COMMA 51 /* ,/< */ +#define DGFX_KEY_DOT 52 /* ./> */ +#define DGFX_KEY_SLASH 53 /* / / ? */ +#define DGFX_KEY_RSHIFT 54 /* right shift */ +#define DGFX_KEY_ALT 56 /* alt key */ +#define DGFX_KEY_SPACE 57 /* space bar */ +#define DGFX_KEY_F1 59 /* f1 */ +#define DGFX_KEY_F2 60 /* f2 */ +#define DGFX_KEY_F3 61 /* f3 */ +#define DGFX_KEY_F4 62 /* f4 */ +#define DGFX_KEY_F5 63 /* f5 */ +#define DGFX_KEY_F6 64 /* f6 */ +#define DGFX_KEY_F7 65 /* f7 */ +#define DGFX_KEY_F8 66 /* f8 */ +#define DGFX_KEY_F9 67 /* f9 */ +#define DGFX_KEY_F10 68 /* f10 */ + +#define DGFX_KEY_HOME 71 /* home */ +#define DGFX_KEY_PAGEUP 73 /* page up */ +#define DGFX_KEY_END 79 /* end */ +#define DGFX_KEY_PAGEDOWN 81 /* page down */ +#define DGFX_KEY_INSERT 82 /* insert */ +#define DGFX_KEY_DELETE 83 /* delete */ + +#define DGFX_KEY_KPSUB 74 /* keypad - */ +#define DGFX_KEY_KPADD 78 /* keypad + */ + +#define DGFX_KEY_F11 87 /* f11 */ +#define DGFX_KEY_F12 88 /* f12 (emergency exit) */ + +#endif /* _SCI_DOS_H_ */ diff --git a/engines/sci/include/sci_memory.h b/engines/sci/include/sci_memory.h new file mode 100644 index 0000000000..41e5aa35ac --- /dev/null +++ b/engines/sci/include/sci_memory.h @@ -0,0 +1,381 @@ +/*************************************************************************** + sci_memory.h Copyright (C) 2001 Alexander R Angas + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Alexander R Angas (Alex Angas) + + History: + + 20010815 - assembled from the various memory allocation functions lying + about, namely resource.h, tools.c (for repeated allocation + attempts), menubar.h (for malloc_cpy, malloc_ncpy). + -- Alex Angas + +***************************************************************************/ + + +/** This header file defines a portable library for allocating memory safely + ** throughout FreeSCI. + ** Implementations of basic functions found here are in this file and + ** $(SRCDIR)/src/scicore/sci_memory.c + * + * Usage notes: + ************** + * + * Define MALLOC_DEBUG to output debug information whenever a memory + * allocation function is called. + * + * Make sure you #define it before any #includes. + * + * #define MALLOC_DEBUG + * #include <...> + * + ************** + * + * Define WITH_DMALLOC to use the dmalloc debug library, available from + * http://dmalloc.com/ + * + ************** + * + * Sets behaviour if memory allocation call fails. + * UNCHECKED_MALLOCS: use C library routine without checks + * (nothing defined): check mallocs and exit immediately on fail (recommended) + * + ** -- Alex Angas + ** + **/ + + +#ifndef _SCI_MEMORY_H +#define _SCI_MEMORY_H + +#include +#include +#include + +#ifdef _WIN32 +# undef scim_inline /* just to be sure it is not defined */ +# define scim_inline __inline +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199900L +# define scim_inline +#else +# define scim_inline inline +#endif + +/********** macros for error messages **********/ + +/* + * Prints an error message. + */ +#define PANIC(prn)\ + fprintf prn; + +/* + * Called if memory allocation fails. + */ +#ifdef WITH_DMALLOC +# ifdef __unix__ +# define DISABLE_SCI_MEMORY /* Use malloc() and friends */ +# endif +#define PANIC_MEMORY(size, filename, linenum, funcname, more_info)\ + PANIC((stderr, "Memory allocation of %lu bytes failed\n"\ + " [%s (%s) : %u]\n " #more_info "\n %s\n",\ + (unsigned long)size, filename, funcname, linenum, dmalloc_strerror(dmalloc_errno))) +#else +#define PANIC_MEMORY(size, filename, linenum, funcname, more_info)\ + PANIC((stderr, "Memory allocation of %lu bytes\n"\ + " [%s (%s) : %u]\n " #more_info "\n %s\n",\ + (unsigned long)size, filename, funcname, linenum, strerror(errno))) +#endif + + +/********** the memory allocation macros **********/ + +#define INFO_MEMORY(alloc_statement, size, filename, linenum, funcname)\ + fprintf(stderr, "ALLOC_MEM(%lu bytes) [%s (%s) : %u] "\ + #alloc_statement "\n",\ + (unsigned long)size, filename, funcname, linenum); + + +#ifdef UNCHECKED_MALLOCS + +#define ALLOC_MEM(alloc_statement, size, filename, linenum, funcname)\ +do {\ + alloc_statement;\ +} while (0); + +#else /* !UNCHECKED_MALLOCS */ + +#define ALLOC_MEM(alloc_statement, size, filename, linenum, funcname)\ +do {\ + if (size < 0)\ + {\ + PANIC_MEMORY(size, filename, linenum, funcname, "Cannot allocate negative bytes of memory!")\ + BREAKPOINT()\ + }\ + else if (size == 0)\ + {\ + PANIC_MEMORY(size, filename, linenum, funcname, "WARNING: allocating zero bytes of memory.")\ + }\ +\ + alloc_statement; /* attempt to allocate the memory */\ +\ + if (res == NULL)\ + {\ + /* exit immediately */\ + PANIC_MEMORY(size, filename, linenum, funcname, "Failed! Exiting...")\ + BREAKPOINT()\ +\ + /* attempt to allocate memory indefinitely\ + do\ + {\ + PANIC_MEMORY(size, filename, linenum, funcname, "Failed! Trying again...");\ + sleep(1000);\ + alloc_statement;\ + } while (res == NULL);\ + */\ + }\ +} while (0); + +#endif /* !UNCHECKED_MALLOCS */ + + +/********** memory allocation routines **********/ + +extern void * +_SCI_MALLOC(size_t size, const char *file, int line, const char *funct); +/* Allocates the specified amount of memory. +** Parameters: (size_t) size: Number of bytes to allocate +** (char *) file: Filename this routine is called from +** (use __FILE__) +** (int) line: Line number this routine is called from +** (use __LINE__) +** (char *) funct: Function this routine is called from +** (use __PRETTY_FUNCTION__ if available) +** Returns : (void *) A pointer to the allocated memory chunk +** To free this string, use the sci_free() command. +** If the call fails, behaviour is dependent on the definition of SCI_ALLOC. +*/ + +extern void * +_SCI_CALLOC(size_t num, size_t size, const char *file, int line, const char *funct); +/* Allocates num * size bytes of zeroed-out memory. +** Parameters: (size_t) num: Number of elements to allocate +** (size_t) size: Amount of memory per element to allocate +** Please see _SCI_MALLOC() for details on other parameters. +** Returns : (void *) A pointer to the allocated memory chunk +** To free this string, use the sci_free() command. +** See _SCI_MALLOC() for more information if call fails. +*/ + +extern void * +_SCI_REALLOC(void *ptr, size_t size, const char *file, int line, const char *funct); +/* Increases the size of an allocated memory chunk. +** Parameters: (void *) ptr: The original pointer +** (size_t) size: New size of the memory chunk +** Please see _SCI_MALLOC() for details on other parameters. +** Returns : (void *) A possibly new pointer, containing 'size' +** bytes of memory and everything contained in the original 'ptr' +** (possibly minus some trailing data if the new memory area is +** smaller than the old one). +** To free this string, use the sci_free() command. +** See _SCI_MALLOC() for more information if call fails. +*/ + +extern void +_SCI_FREE(void *ptr, const char *file, int line, const char *funct); +/* Frees previously allocated memory chunks +** Parameters: (void *) ptr: The pointer to free +** Please see _SCI_MALLOC() for details on other parameters. +** Returns : (void) +*/ + +extern void * +_SCI_MEMDUP(const void *src, size_t size, const char *file, int line, const char *funct); +/* Duplicates a chunk of memory +** Parameters: (void *) src: Pointer to the data to duplicate +** (size_t) size: Number of bytes to duplicate +** Please see _SCI_MALLOC() for details on other parameters. +** Returns : (void *) An appropriately allocated duplicate, or NULL on error +** Please try to avoid data duplication unless absolutely neccessary! +** To free this string, use the sci_free() command. +** See _SCI_MALLOC() for more information if call fails. +*/ + +extern char * +_SCI_STRDUP(const char *src, const char *file, int line, const char *funct); +/* Duplicates a string. +** Parameters: (const char *) src: The original pointer +** Please see _SCI_MALLOC() for details on other parameters. +** Returns : (char *) a pointer to the storage location for the copied +** string. +** To free this string, use the sci_free() command. +** See _SCI_MALLOC() for more information if call fails. +*/ + + +extern char * +_SCI_STRNDUP(const char *src, size_t length, const char *file, int line, const char *funct); +/* Copies a string into a newly allocated memory part, up to a certain length. +** Parameters: (char *) src: The source string +** (int) length: The maximum length of the string (not counting +** a trailing \0). +** Please see _SCI_MALLOC() for details on other parameters. +** Returns : (char *) The resulting copy, allocated with sci_malloc(). +** To free this string, use the sci_free() command. +** See _SCI_MALLOC() for more information if call fails. +*/ + +/****************************************/ +/* Refcounting garbage collected memory */ +/****************************************/ + +/* Refcounting memory calls are a little slower than the others, +** and using it improperly may cuase memory leaks. It conserves +** memory, though. */ + +#define SCI_REFCOUNT_TEST(f) sci_refcount_incref(f); sci_refcount_decref(f); + +extern void * +sci_refcount_alloc(size_t length); +/* Allocates "garbage" memory +** Parameters: (size_t) length: Number of bytes to allocate +** Returns : (void *) The allocated memory +** Memory allocated in this fashion will be marked as holding one reference. +** It cannot be freed with 'free()', only by using sci_refcount_decref(). +*/ + +extern void * +sci_refcount_incref(void *data); +/* Adds another reference to refcounted memory +** Parameters: (void *) data: The data to add a reference to +** Returns : (void *) data +*/ + +extern void +sci_refcount_decref(void *data); +/* Decrements the reference count for refcounted memory +** Parameters: (void *) data: The data to add a reference to +** Returns : (void *) data +** If the refcount reaches zero, the memory will be deallocated +*/ + +extern void * +sci_refcount_memdup(void *data, size_t len); +/* Duplicates non-refcounted memory into a refcounted block +** Parameters: (void *) data: The memory to copy from +** (size_t) len: The number of bytes to copy/allocate +** Returns : (void *) Newly allocated refcounted memory +** The number of references accounted for will be one. +*/ + +/********** macro definitions for routines **********/ + +#ifdef DISABLE_SCI_MEMORY +# define sci_malloc malloc +# define sci_calloc calloc +# define sci_realloc realloc +# define sci_free free +# define sci_strdup strdup +# define sci_strndup strndup +# define sci_memdup memdup +#else + +# ifdef __GNUC__ +# define sci_malloc(size)\ + _SCI_MALLOC(size, __FILE__, __LINE__, __PRETTY_FUNCTION__) +# else +# define sci_malloc(size)\ + _SCI_MALLOC(size, __FILE__, __LINE__, "") +# endif + +# ifdef __GNUC__ +# define sci_calloc(num, count)\ + _SCI_CALLOC(num, count, __FILE__, __LINE__, __PRETTY_FUNCTION__) +# else +# define sci_calloc(num, count)\ + _SCI_CALLOC(num, count, __FILE__, __LINE__, "") +# endif + + +# ifdef __GNUC__ +# define sci_realloc(ptr, size)\ + _SCI_REALLOC(ptr, size, __FILE__, __LINE__, __PRETTY_FUNCTION__) +# else +# define sci_realloc(ptr, size)\ + _SCI_REALLOC(ptr, size, __FILE__, __LINE__, "") +# endif + + +# ifdef __GNUC__ +# define sci_free(ptr)\ + _SCI_FREE(ptr, __FILE__, __LINE__, __PRETTY_FUNCTION__) +# else +# define sci_free(ptr)\ + _SCI_FREE(ptr, __FILE__, __LINE__, "") +# endif + + +# ifdef __GNUC__ +# define sci_memdup(src, size)\ + _SCI_MEMDUP(src, size, __FILE__, __LINE__, __PRETTY_FUNCTION__) +# else +# define sci_memdup(src, size)\ + _SCI_MEMDUP(src, size, __FILE__, __LINE__, "") +# endif + + +# ifdef __GNUC__ +# define sci_strdup(src)\ + _SCI_STRDUP(src, __FILE__, __LINE__, __PRETTY_FUNCTION__) +# else +# define sci_strdup(src)\ + _SCI_STRDUP(src, __FILE__, __LINE__, "") +# endif + + +# ifdef __GNUC__ +# define sci_strndup(src, length)\ + _SCI_STRNDUP(src, length, __FILE__, __LINE__, __PRETTY_FUNCTION__) +# else +# define sci_strndup(src, length)\ + _SCI_STRNDUP(src, length, __FILE__, __LINE__, "") +# endif +#endif + + +/********** other memory/debug related routines **********/ + +#ifdef _WIN32 +extern void +debug_win32_memory(int dbg_setting); +/* Sets debugging for Win32 memory. +** Parameters: (bool) dbg_setting: +** 0: no debugging +** 1: call _CrtCheckMemory at every memory request +** 2: perform automatic leak checking at program exit +** 3: enable debug heap allocations +*/ +#endif + + +#endif /* _SCI_MEMORY_H */ diff --git a/engines/sci/include/sci_midi.h b/engines/sci/include/sci_midi.h new file mode 100644 index 0000000000..16003a3cbd --- /dev/null +++ b/engines/sci/include/sci_midi.h @@ -0,0 +1,53 @@ +/*************************************************************************** + sci_midi.h Copyright (C) 2003 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef _SCI_MIDI_H_ +#define _SCI_MIDI_H_ + +#define MIDI_RHYTHM_CHANNEL 9 + +/* Special SCI sound stuff */ + +#define SCI_MIDI_TIME_EXPANSION_PREFIX 0xF8 +#define SCI_MIDI_TIME_EXPANSION_LENGTH 240 + +#define SCI_MIDI_EOT 0xFC +#define SCI_MIDI_SET_SIGNAL 0xCF +#define SCI_MIDI_SET_POLYPHONY 0x4B +#define SCI_MIDI_RESET_ON_SUSPEND 0x4C +#define SCI_MIDI_CHANNEL_MUTE 0x4E +#define SCI_MIDI_SET_REVERB 0x50 +#define SCI_MIDI_HOLD 0x52 +#define SCI_MIDI_CUMULATIVE_CUE 0x60 +#define SCI_MIDI_CHANNEL_NOTES_OFF 0x7B /* all-notes-off for Bn */ + +#define SCI_MIDI_SET_SIGNAL_LOOP 0x7F +/* If this is the parameter of 0xCF, the loop point is set here */ + +#define SCI_MIDI_CONTROLLER(status) ((status & 0xF0) == 0xB0) + +#endif /* !defined(_SCI_MIDI_H_) */ diff --git a/engines/sci/include/sci_widgets.h b/engines/sci/include/sci_widgets.h new file mode 100644 index 0000000000..f961021622 --- /dev/null +++ b/engines/sci/include/sci_widgets.h @@ -0,0 +1,211 @@ +/*************************************************************************** + sci_widgets.h Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* SCI-specific widget handling */ + +#ifndef _SCI_WIDGETS_H_ +#define _SCI_WIDGETS_H_ + +#include + +/* The following flags are applicable to windows in SCI0: */ +#define WINDOW_FLAG_TRANSPARENT 0x01 + + +#define WINDOW_FLAG_NOFRAME 0x02 +/* No frame is drawn around the window onto wm_view */ + +#define WINDOW_FLAG_TITLE 0x04 +/* Add titlebar to window (10 pixels high, framed, text is centered and written +** in white on dark gray +*/ + +#define WINDOW_FLAG_DONTDRAW 0x80 +/* Don't draw anything */ + +#define WINDOW_FLAG_NO_DROP_SHADOW 0x1000000 + + +/* Used in kgraphics to flag text surrounded by a dithered frame */ +#define CONTROL_STATE_DITHER_FRAMED 0x1000 + +/* Used by the interpreter to flag buttons that are grayed out */ +#define CONTROL_STATE_GRAY 0x0004 +/* Used by the interpreter to flag some widgets to determine whether they should be surrounded by a frame */ +#define CONTROL_STATE_FRAMED 0x0008 +/* Used by the interpreter to flag buttons that are enabled */ +#define CONTROL_STATE_ENABLED 0x0001 + +void +sciw_set_status_bar(state_t *s, gfxw_port_t *status_bar, char *text, int fgcolor, int bgcolor); +/* Sets the contents of a port used as status bar +** Parmeters: (state_t *) s: The affected game state +** (gfxw_port_t *) status_bar: The status bar port +** (char *) text: The text to draw +** Returns : (void) +*/ + +gfxw_port_t * +sciw_new_window(state_t *s, rect_t area, int font, gfx_color_t color, gfx_color_t bgcolor, + int title_font, gfx_color_t title_color, gfx_color_t title_bg_color, + const char *title, int flags); +/* Creates a new SCI style window +** Parameters: (state_t *) s: The affected game state +** (rect_t) area: The screen area to frame (not including a potential window title) +** (int) font: Default font number to use +** (gfx_color_t) color: The foreground color to use for drawing +** (gfx_color_t) bgcolor: The background color to use +** (int) title_font: The font to use for the title bar (if any) +** (gfx_color_t) title_color: Color to use for the title bar text +** (gfx_color_t) title_bg_color: Color to use for the title bar background +** (const char *) title: The text to write into the title bar +** (int) flags: Any ORred combination of window flags +** Returns : (gfxw_port_t *) A newly allocated port with the requested characteristics +*/ + +/*---------------------*/ +/*** Control widgets ***/ +/*---------------------*/ + +gfxw_list_t * +sciw_new_button_control(gfxw_port_t *port, reg_t ID, rect_t zone, char *text, int font, char selected, char inverse, char gray); +/* Creates a new button control list +** Parameters: (gfxw_port_t *) port: The port containing the color values to use for the +** button (the button is /not/ appended to the port there) +** (reg_t) ID: Button's ID +** (rect_t) zone: The area occupied by the button +** (char *) text: The text to write into the button +** (int) font: The font to use for the button +** (char) selected: Whether the button should be marked as being selected by the keyboard focus +** (char) inverse: Whether to inverse the color scheme +** (char) gray: Whether the button should be grayed out +** Returns : (gfxw_list_t *) The button +*/ + +gfxw_list_t * +sciw_new_text_control(gfxw_port_t *port, reg_t ID, rect_t zone, char *text, int font, + gfx_alignment_t align, char frame, char inverse); +/* Creates a new text control list +** Parameters: (gfxw_port_t *) port: The port containing the color values to use +** (reg_t) ID: Text widget ID +** (rect_t) zone: Area occupied by the text +** (char *) text: The text +** (int) font: The font the text is to be drawn in +** (gfx_alignment_t) align: Horizontal text alignment to use +** (char) frame: Whether a dithered frame should surround the text +** (char) inverse: Whether the text colors should be inversed +** Returns : (gfxw_list_t *) The text control widget list +*/ + +gfxw_list_t * +sciw_new_edit_control(gfxw_port_t *port, reg_t ID, rect_t zone, char *text, int font, unsigned int cursor, + char inverse); +/* Creates a new edit control list +** Parameters: (gfxw_port_t *) port: The port containing the color values to use +** (reg_t) ID: Text widget ID +** (rect_t) zone: Area occupied by the text +** (char *) text: The text +** (int) font: The font the text is to be drawn in +** (int) cursor: Cursor position +** (char) inverse: Whether the edit widget should be reversed +** Returns : (gfxw_list_t *) An appropriate widget list +*/ + +gfxw_list_t * +sciw_new_icon_control(gfxw_port_t *port, reg_t ID, rect_t zone, int view, int loop, int cel, + char frame, char inverse); +/* Creates a new icon control list +** Parameters: (gfxw_port_t *) port: The port containing the color values to use +** (reg_t) ID: Text widget ID +** (rect_t) zone: Area occupied by the text +** (int x int x int) view, loop, cel: The cel to display +** (char) frame: Whether the widget should be surrounded by a frame +** (char) lina inverse: Whether colors should be inversed +** Returns : (gfxw_list_t *) An appropriate widget list +*/ + +gfxw_list_t * +sciw_new_list_control(gfxw_port_t *port, reg_t ID, rect_t zone, int font_nr, char **entries_list, + int entries_nr, int list_top, int selection, char inverse); +/* Creates a new list control list +** Parameters: (gfxw_port_t *) port: The port containing the color values to use +** (int) ID: Text widget ID +** (rect_t) zone: Area occupied by the text +** (int) font_nr: number of the font to use +** (char **) entries_list: List of strings to contain within the list +** (int) entries_nr: Number of entries in entries_list +** (int) list_top: First list item that is visible +** (int) selection: The list item that is selected +** (char) invserse: The usual meaning +** Returns : (gfxw_list_t *) An appropriate widget list +*/ + +/*---------------------*/ +/*** Menubar widgets ***/ +/*---------------------*/ + +void +sciw_set_menubar(state_t *s, gfxw_port_t *status_bar, menubar_t *menubar, int selection); +/* Draws the menu bar +** Parameters: (state_t *) s: The state to operate on +** (gfxw_port_t *) status_bar: The status bar port to modify +** (menubar_t *) menubar: The menu bar to use +** (int) selection: Number of the menu to hightlight, or -1 for 'none' +** Returns : (void) +*/ + +gfxw_port_t * +sciw_new_menu(state_t *s, gfxw_port_t *status_bar, menubar_t *menubar, int selection); +/* Creates a menu port +** Parameters: (state_t *) s: The state to operate on +** (gfxw_port_t *) status_bar: The status bar +** (menubar_t *) menubar: The menu bar to use +** (int) selection: Number of the menu to interpret +** Returns : (gfxw_port_t *) The result port +*/ + +gfxw_port_t * +sciw_unselect_item(state_t *s, gfxw_port_t *menu_port, menu_t *menu, int selection); +/* Unselects a previously selected item from a menu port +** Parameters: (state_t *) s: The state to operate on +** (gfxw_port_t *) menu_port: The port modify +** (menu_t *) menu: The menu the menu port corresponds to +** (int) selection: Number of the menu entry to unselect, or -1 to do a NOP +** Returns : (gfxw_port_t *) The modified menu +*/ + +gfxw_port_t * +sciw_select_item(state_t *s, gfxw_port_t *menu_port, menu_t *menu, int selection); +/* Selects a menu item from a menu port +** Parameters: (state_t *) s: The state to operate on +** (gfxw_port_t *) menu_port: The port modify +** (menu_t *) menu: The menu the menu port corresponds to +** (int) selection: Number of the menu entry to select, or -1 to do a NOP +** Returns : (gfxw_port_t *) The modified menu +*/ + +#endif /* _!SCI_WIDGETS_H_ */ + diff --git a/engines/sci/include/sciresource.h b/engines/sci/include/sciresource.h new file mode 100644 index 0000000000..e9ac7cf5d4 --- /dev/null +++ b/engines/sci/include/sciresource.h @@ -0,0 +1,538 @@ +/*************************************************************************** + sciresource.h Copyright (C) 1999,2000,01 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) [creichen@rbg.informatik.tu-darmstadt.de] + + History: + + 990327 - created (CR) + +***************************************************************************/ + +#ifndef _SCIRESOURCE_H_ +#define _SCIRESOURCE_H_ + +/*#define _SCI_RESOURCE_DEBUG */ +/*#define _SCI_DECOMPRESS_DEBUG*/ + +#include +#include + +#define SCI_MAX_RESOURCE_SIZE 0x0400000 +/* The maximum allowed size for a compressed or decompressed resource */ + +#ifdef _WIN32 +# define DIR_SEPARATOR_STR "\\" +# define PATH_SEPARATOR_STR ";" +#else +# define DIR_SEPARATOR_STR "/" +# define PATH_SEPARATOR_STR ":" +#endif + + +/*** RESOURCE STATUS TYPES ***/ +#define SCI_STATUS_NOMALLOC 0 +#define SCI_STATUS_ALLOCATED 1 +#define SCI_STATUS_ENQUEUED 2 /* In the LRU queue */ +#define SCI_STATUS_LOCKED 3 /* Allocated and in use */ + +#define SCI_RES_FILE_NR_PATCH -1 /* Resource was read from a patch file rather than from a resource */ + + +/*** INITIALIZATION RESULT TYPES ***/ +#define SCI_ERROR_IO_ERROR 1 +#define SCI_ERROR_EMPTY_OBJECT 2 +#define SCI_ERROR_INVALID_RESMAP_ENTRY 3 +/* Invalid resource.map entry */ +#define SCI_ERROR_RESMAP_NOT_FOUND 4 +#define SCI_ERROR_NO_RESOURCE_FILES_FOUND 5 +/* No resource at all was found */ +#define SCI_ERROR_UNKNOWN_COMPRESSION 6 +#define SCI_ERROR_DECOMPRESSION_OVERFLOW 7 +/* decompression failed: Buffer overflow (wrong SCI version?) */ +#define SCI_ERROR_DECOMPRESSION_INSANE 8 +/* sanity checks failed during decompression */ +#define SCI_ERROR_RESOURCE_TOO_BIG 9 +/* Resource size exceeds SCI_MAX_RESOURCE_SIZE */ +#define SCI_ERROR_UNSUPPORTED_VERSION 10 +#define SCI_ERROR_INVALID_SCRIPT_VERSION 11 + +#define SCI_ERROR_CRITICAL SCI_ERROR_NO_RESOURCE_FILES_FOUND +/* the first critical error number */ + +/*** SCI VERSION NUMBERS ***/ +#define SCI_VERSION_AUTODETECT 0 +#define SCI_VERSION_0 1 +#define SCI_VERSION_01 2 +#define SCI_VERSION_01_VGA 3 +#define SCI_VERSION_01_VGA_ODD 4 +#define SCI_VERSION_1_EARLY 5 +#define SCI_VERSION_1_LATE 6 +#define SCI_VERSION_1_1 7 +#define SCI_VERSION_32 8 +#define SCI_VERSION_LAST SCI_VERSION_1_LATE /* The last supported SCI version */ + +#define SCI_VERSION_1 SCI_VERSION_1_EARLY + +#define RESSOURCE_TYPE_DIRECTORY 0 +#define RESSOURCE_TYPE_AUDIO_DIRECTORY 1 +#define RESSOURCE_TYPE_VOLUME 2 +#define RESSOURCE_TYPE_EXTERNAL_MAP 3 +#define RESSOURCE_TYPE_INTERNAL_MAP 4 +#define RESSOURCE_TYPE_MASK 127 +#define RESSOURCE_ADDRESSING_BASIC 0 +#define RESSOURCE_ADDRESSING_EXTENDED 128 +#define RESSOURCE_ADDRESSING_MASK 128 + +extern DLLEXTERN const char* sci_error_types[]; +extern DLLEXTERN const char* sci_version_types[]; +extern DLLEXTERN const char* sci_resource_types[]; +extern DLLEXTERN const char* sci_resource_type_suffixes[]; /* Suffixes for SCI1 patch files */ +extern DLLEXTERN const int sci_max_resource_nr[]; /* Highest possible resource numbers */ + + +enum ResourceTypes { + sci_view=0, sci_pic, sci_script, sci_text, + sci_sound, sci_memory, sci_vocab, sci_font, + sci_cursor, sci_patch, sci_bitmap, sci_palette, + sci_cdaudio, sci_audio, sci_sync, sci_message, + sci_map, sci_heap, sci_invalid_resource +}; + +#define sci0_last_resource sci_patch +#define sci1_last_resource sci_heap +/* Used for autodetection */ + + +struct resource_index_struct { + unsigned short resource_id; + unsigned int resource_location; +}; /* resource type as stored in the resource.map file */ + +typedef struct resource_index_struct resource_index_t; + +typedef struct resource_source_struct { + int source_type; + int scanned; + union { + struct { + char *name; + int volume_number; + } file; + struct { + char *name; + } dir; + struct { + struct _resource_struct *resource; + } internal_map; + } location; + struct resource_source_struct *associated_map; + struct resource_source_struct *next; +} resource_source_t; + +typedef struct _resource_altsource_struct { + resource_source_t *source; + unsigned int file_offset; + struct _resource_altsource_struct *next; +} resource_altsource_t; + + +typedef struct _resource_struct { + unsigned char *data; + + unsigned short number; + unsigned short type; + guint16 id; /* contains number and type */ + + unsigned int size; + + unsigned int file_offset; /* Offset in file */ + resource_source_t *source; + + unsigned char status; + unsigned short lockers; /* Number of places where this resource was locked */ + + struct _resource_struct *next; /* Position marker for the LRU queue */ + struct _resource_struct *prev; + + resource_altsource_t *alt_sources; /* SLL of alternative resource data sources */ +} resource_t; /* for storing resources in memory */ + + +typedef struct { + int max_memory; /* Config option: Maximum total byte number allocated */ + int sci_version; /* SCI resource version to use */ + + int resources_nr; + resource_source_t *sources; + resource_t *resources; + + int memory_locked; /* Amount of resource bytes in locked memory */ + int memory_lru; /* Amount of resource bytes under LRU control */ + + char *resource_path; /* Path to the resource and patch files */ + + resource_t *lru_first, *lru_last; /* Pointers to the first and last LRU queue entries */ + /* LRU queue: lru_first points to the most recent entry */ + + unsigned char allow_patches; +} resource_mgr_t; + +/**** FUNCTION DECLARATIONS ****/ + +/**--- New Resource manager ---**/ + +resource_mgr_t * +scir_new_resource_manager(char *dir, int version, char allow_patches, int max_memory); +/* Creates a new FreeSCI resource manager +** Parameters: (char *) dir: Path to the resource and patch files (not modified or freed +** by the resource manager) +** (int) version: The SCI version to look for; use SCI_VERSION_AUTODETECT +** in the default case. +** (char ) allow_patches: Set to 1 if external patches (those look like +** "view.101" or "script.093") should be applied +** (int) max_memory: Maximum number of bytes to allow allocated for resources +** Returns : (resource_mgr_t *) A newly allocated resource manager +** max_memory will not be interpreted as a hard limit, only as a restriction for resources +** which are not explicitly locked. However, a warning will be issued whenever this limit +** is exceeded. +*/ + +resource_source_t * +scir_add_patch_dir(resource_mgr_t *mgr, int type, char *path); +/* Add a path to the resource manager's list of sources. +** Parameters: (resource_mgr_t *) mgr: The resource manager to look up in +** (int) dirtype: The type of patch directory to add, +** either RESSOURCE_TYPE_DIRECTORY or RESSOURCE_TYPE_AUDIO_DIRECTORY +** (char *) path: The path to add +** Returns: A pointer to the added source structure, or NULL if an error occurred. +*/ + +resource_source_t * +scir_get_volume(resource_mgr_t *mgr, resource_source_t *map, int volume_nr); + +resource_source_t * +scir_add_volume(resource_mgr_t *mgr, resource_source_t *map, char *filename, + int number, int extended_addressing); +/* Add a volume to the resource manager's list of sources. +** Parameters: (resource_mgr_t *) mgr: The resource manager to look up in +** (resource_source_t *) map: The map associated with this volume +** (char *) filename: The name of the volume to add +** (int) extended_addressing: 1 if this volume uses extended addressing, +** 0 otherwise. +** Returns: A pointer to the added source structure, or NULL if an error occurred. +*/ + +resource_source_t * +scir_add_external_map(resource_mgr_t *mgr, char *file_name); +/* Add an external (i.e. separate file) map resource to the resource manager's list of sources. +** Parameters: (resource_mgr_t *) mgr: The resource manager to look up in +** (char *) file_name: The name of the volume to add +** Returns: A pointer to the added source structure, or NULL if an error occurred. +*/ + +resource_source_t * +scir_add_internal_map(resource_mgr_t *mgr, resource_t *map); +/* Add an internal (i.e. a resource) map resource to the resource manager's list of sources. +** Parameters: (resource_mgr_t *) mgr: The resource manager to look up in +** (char *) file_name: The name of the volume to add +** Returns: A pointer to the added source structure, or NULL if an error occurred. +*/ + +int +scir_scan_new_sources(resource_mgr_t *mgr, int *detected_version); +/* Scans newly registered resource sources for resources, earliest addition first. +** Parameters: (resource_mgr_t *) mgr: The resource manager to look up in +** (int *) detected_version: Pointer to the detected version number, +** used during startup. May be NULL. +** Returns: One of SCI_ERROR_*. +*/ + +resource_t * +scir_find_resource(resource_mgr_t *mgr, int type, int number, int lock); +/* Looks up a resource's data +** Parameters: (resource_mgr_t *) mgr: The resource manager to look up in +** (int) type: The resource type to look for +** (int) number: The resource number to search +** (int) lock: non-zero iff the resource should be locked +** Returns : (resource_t *): The resource, or NULL if it doesn't exist +** Locked resources are guaranteed not to have their contents freed until +** they are unlocked explicitly (by scir_unlock_resource). +*/ + +void +scir_unlock_resource(resource_mgr_t *mgr, resource_t *res, int restype, int resnum); +/* Unlocks a previously locked resource +** Parameters: (resource_mgr_t *) mgr: The manager the resource should be freed from +** (resource_t *) res: The resource to free +** (int) type: Type of the resource to check (for error checking) +** (int) number: Number of the resource to check (ditto) +** Returns : (void) +*/ + +resource_t * +scir_test_resource(resource_mgr_t *mgr, int type, int number); +/* Tests whether a resource exists +** Parameters: (resource_mgr_t *) mgr: The resource manager to search in +** (int) type: Type of the resource to check +** (int) number: Number of the resource to check +** Returns : (resource_t *) non-NULL if the resource exists, NULL otherwise +** This function may often be much faster than finding the resource +** and should be preferred for simple tests. +** The resource object returned is, indeed, the resource in question, but +** it should be used with care, as it may be unallocated. +** Use scir_find_resource() if you want to use the data contained in the resource. +*/ + +void +scir_free_resource_manager(resource_mgr_t *mgr); +/* Frees a resource manager and all memory handled by it +** Parameters: (resource_mgr_t *) mgr: The Manager to free +** Returns : (void) +*/ + +/**--- Resource map decoding functions ---*/ + +int +sci0_read_resource_map(resource_mgr_t *mgr, resource_source_t *map, resource_t **resources, int *resource_nr_p, int *sci_version); +/* Reads the SCI0 resource.map file from a local directory +** Parameters: (char *) path: (unused) +** (resource_t **) resources: Pointer to a pointer +** that will be set to the +** location of the resources +** (in one large chunk) +** (int *) resource_nr_p: Pointer to an int the number of resources +** read is stored in +** (int) sci_version: SCI resource version +** Returns : (int) 0 on success, an SCI_ERROR_* code otherwise +*/ + +int +sci1_read_resource_map(resource_mgr_t *mgr, resource_source_t *map, resource_source_t *vol, + resource_t **resource_p, int *resource_nr_p, int *sci_version); +/* Reads the SCI1 resource.map file from a local directory +** Parameters: (char *) path: (unused) +** (resource_t **) resources: Pointer to a pointer +** that will be set to the +** location of the resources +** (in one large chunk) +** (int *) resource_nr_p: Pointer to an int the number of resources +** read is stored in +** (int) sci_version: SCI resource version +** Returns : (int) 0 on success, an SCI_ERROR_* code otherwise +*/ + +/**--- Patch management functions ---*/ + +void +sci0_sprintf_patch_file_name(char *string, resource_t *res); +/* Prints the name of a matching patch to a string buffer +** Parameters: (char *) string: The buffer to print to +** (resource_t *) res: Resource containing the number and type of the +** resource whose name is to be print +** Returns : (void) +*/ + +void +sci1_sprintf_patch_file_name(char *string, resource_t *res); +/* Prints the name of a matching patch to a string buffer +** Parameters: (char *) string: The buffer to print to +** (resource_t *) res: Resource containing the number and type of the +** resource whose name is to be print +** Returns : (void) +*/ + +int +sci0_read_resource_patches(resource_source_t *source, resource_t **resources, int *resource_nr_p); +/* Reads SCI0 patch files from a local directory +** Parameters: (char *) path: (unused) +** (resource_t **) resources: Pointer to a pointer +** that will be set to the +** location of the resources +** (in one large chunk) +** (int *) resource_nr_p: Pointer to an int the number of resources +** read is stored in +** Returns : (int) 0 on success, an SCI_ERROR_* code otherwise +*/ + +int +sci1_read_resource_patches(resource_source_t *source, resource_t **resources, int *resource_nr_p); +/* Reads SCI1 patch files from a local directory +** Parameters: (char *) path: (unused) +** (resource_t **) resources: Pointer to a pointer +** that will be set to the +** location of the resources +** (in one large chunk) +** (int *) resource_nr_p: Pointer to an int the number of resources +** read is stored in +** Returns : (int) 0 on success, an SCI_ERROR_* code otherwise +*/ + + +/**--- Decompression functions ---**/ + + +int decompress0(resource_t *result, int resh, int sci_version); +/* Decrypts resource data and stores the result for SCI0-style compression. +** Parameters : result: The resource_t the decompressed data is stored in. +** resh : File handle of the resource file +** sci_version : Actual SCI resource version +** Returns : (int) 0 on success, one of SCI_ERROR_* if a problem was +** encountered. +*/ + +int decompress01(resource_t *result, int resh, int sci_version); +/* Decrypts resource data and stores the result for SCI01-style compression. +** Parameters : result: The resource_t the decompressed data is stored in. +** resh : File handle of the resource file +** sci_version : Actual SCI resource version +** Returns : (int) 0 on success, one of SCI_ERROR_* if a problem was +** encountered. +*/ + +int decompress1(resource_t *result, int resh, int sci_version); +/* Decrypts resource data and stores the result for SCI1.1-style compression. +** Parameters : result: The resource_t the decompressed data is stored in. +** sci_version : Actual SCI resource version +** resh : File handle of the resource file +** Returns : (int) 0 on success, one of SCI_ERROR_* if a problem was +** encountered. +*/ + + +int decompress11(resource_t *result, int resh, int sci_version); +/* Decrypts resource data and stores the result for SCI1.1-style compression. +** Parameters : result: The resource_t the decompressed data is stored in. +** sci_version : Actual SCI resource version +** resh : File handle of the resource file +** Returns : (int) 0 on success, one of SCI_ERROR_* if a problem was +** encountered. +*/ + + +int decrypt2(guint8* dest, guint8* src, int length, int complength); +/* Huffman token decryptor - defined in decompress0.c and used in decompress01.c +*/ + +int decrypt4(guint8* dest, guint8* src, int length, int complength); +/* DCL inflate- implemented in decompress1.c +*/ + +byte *view_reorder(byte *inbuffer, int dsize); +/* SCI1 style view compression */ + +byte *pic_reorder(byte *inbuffer, int dsize); +/* SCI1 style pic compression */ + +/*--- Internal helper functions ---*/ + +void +_scir_free_resources(resource_t *resources, int resources_nr); +/* Frees a block of resources and associated data +** Parameters: (resource_t *) resources: The resources to free +** (int) resources_nr: Number of resources in the block +** Returns : (void) +*/ + +resource_t * +_scir_find_resource_unsorted(resource_t *res, int res_nr, int type, int number); +/* Finds a resource matching type.number in an unsorted resource_t block +** To be used during initial resource loading, when the resource list +** may not have been sorted yet. +** Parameters: (resource_t *) res: Pointer to the block to search in +** (int) res_nr: Number of resource_t structs allocated and defined +** in the block pointed to by res +** (int) type: Type of the resource to look for +** (int) number: Number of the resource to look for +** Returns : (resource_t) The matching resource entry, or NULL if not found +*/ + +void +_scir_add_altsource(resource_t *res, resource_source_t *source, unsigned int file_offset); +/* Adds an alternative source to a resource +** Parameters: (resource_t *) res: The resource to add to +** (resource_source_t *) source: The source of the resource +** (unsigned int) file_offset: Offset in the file the resource +** is stored at +** Retruns : (void) +*/ + + +/**** Internal #defines ****/ + +#define SCI_RESOURCE_FILE_PATCH -1 /* Identifies resources read from patches */ + +/* Resource type encoding */ +#define SCI0_B1_RESTYPE_MASK 0xf8 +#define SCI0_B1_RESTYPE_SHIFT 3 +#define SCI0_B3_RESFILE_MASK 0xfc +#define SCI0_B3_RESFILE_SHIFT 2 +#define SCI01V_B3_RESFILE_MASK 0xf0 +#define SCI01V_B3_RESFILE_SHIFT 4 + +#define SCI0_RESID_GET_TYPE(bytes) \ + (((bytes)[1] & SCI0_B1_RESTYPE_MASK) >> SCI0_B1_RESTYPE_SHIFT) +#define SCI0_RESID_GET_NUMBER(bytes) \ + ((((bytes)[1] & ~SCI0_B1_RESTYPE_MASK) << 8) | ((bytes)[0])) + +#define SCI0_RESFILE_GET_FILE(bytes) \ + (((bytes)[3] & SCI0_B3_RESFILE_MASK) >> SCI0_B3_RESFILE_SHIFT) +#define SCI0_RESFILE_GET_OFFSET(bytes) \ + ((((bytes)[3] & ~SCI0_B3_RESFILE_MASK) << 24) \ + | (((bytes)[2]) << 16) \ + | (((bytes)[1]) << 8) \ + | (((bytes)[0]) << 0)) + +#define SCI01V_RESFILE_GET_FILE(bytes) \ + (((bytes)[3] & SCI01V_B3_RESFILE_MASK) >> SCI01V_B3_RESFILE_SHIFT) +#define SCI01V_RESFILE_GET_OFFSET(bytes) \ + ((((bytes)[3] & ~SCI01V_B3_RESFILE_MASK) << 24) \ + | (((bytes)[2]) << 16) \ + | (((bytes)[1]) << 8) \ + | (((bytes)[0]) << 0)) + +#define SCI1_B5_RESFILE_MASK 0xf0 +#define SCI1_B5_RESFILE_SHIFT 4 + +#define SCI1_RESFILE_GET_FILE(bytes) \ + (((bytes)[5] & SCI1_B5_RESFILE_MASK) >> SCI1_B5_RESFILE_SHIFT) + +#define SCI1_RESFILE_GET_OFFSET(bytes) \ + ((((bytes)[5] & ~SCI1_B5_RESFILE_MASK) << 24) \ + | (((bytes)[4]) << 16) \ + | (((bytes)[3]) << 8) \ + | (((bytes)[2]) << 0)) + +#define SCI1_RESFILE_GET_NUMBER(bytes) \ + ((((bytes)[1]) << 8) \ + | (((bytes)[0]) << 0)) + +#define SCI11_RESFILE_GET_OFFSET(bytes) \ + ((((bytes)[4]) << 17) \ + | (((bytes)[3]) << 9) \ + | (((bytes)[2]) << 1)) + +#endif + + + diff --git a/engines/sci/include/scitypes.h b/engines/sci/include/scitypes.h new file mode 100644 index 0000000000..b252d59565 --- /dev/null +++ b/engines/sci/include/scitypes.h @@ -0,0 +1,155 @@ +/*************************************************************************** + scitypes.h Copyright (C) 2001 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) [creichen@rbg.informatik.tu-darmstadt.de] + +***************************************************************************/ + +#ifndef SCI_TYPES +#define SCI_TYPES + +#ifdef SCUMMVM + +#include "common/scummsys.h" + +// TODO: rework sci_dir_t to use common/fs.h and remove these includes +#include +#include + +typedef int8 gint8; +typedef uint8 guint8; + +typedef int16 gint16; +typedef uint16 guint16; + +typedef int32 gint32; +typedef uint32 guint32; + +#undef byte + +#else // SCUMMVM + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_DIRENT_H +# include +# include +#endif +#ifdef _WIN32 +# include +# include +#endif +#ifdef _DREAMCAST +# include +#endif + +#if defined(_WIN32) && defined(_MSC_VER) +# define TYPE_16 short +# define TYPE_32 int +#endif + +#ifndef TYPE_8 +# define TYPE_8 char /* Guaranteed by ISO */ +#endif + +#ifndef TYPE_16 +# if (SIZEOF_SHORT == 2) +# define TYPE_16 short +# elif (SIZEOF_INT == 2) +# define TYPE_16 int +# else +# error "Could not find a 16 bit data type!" +# endif +#endif /* !TYPE_16 */ + +#ifndef TYPE_32 +# if (SIZEOF_INT == 4) +# define TYPE_32 int +# elif (SIZEOF_LONG == 4) +# define TYPE_32 long +# else +# error "Could not find a 32 bit data type!" +# endif +#endif /* !TYPE_32 */ + +typedef signed TYPE_8 gint8; +typedef unsigned TYPE_8 guint8; + +typedef signed TYPE_16 gint16; +typedef unsigned TYPE_16 guint16; + +typedef signed TYPE_32 gint32; +typedef unsigned TYPE_32 guint32; + +#undef TYPE_8 +#undef TYPE_16 +#undef TYPE_32 + +#endif // SCUMMVM + +typedef gint8 sbyte; +typedef guint8 byte; +typedef guint16 word; + +typedef struct { + long tv_sec; + long tv_usec; +} GTimeVal; + +typedef struct { +#ifdef _MSC_VER + long search; + struct _finddata_t fileinfo; +#else + DIR *dir; + char *mask_copy; +#endif +} sci_dir_t; /* used by sci_find_first and friends */ + +/* + * Fixed point type, borrowed from ScummVM. + * The precision of the fractional (fixed point) type we define below. + * Normally you should never have to modify this value. + */ +enum { + FRAC_BITS = 16, + FRAC_LO_MASK = ((1L << FRAC_BITS) - 1), + FRAC_HI_MASK = (((1L << (32 - FRAC_BITS)) - 1) << FRAC_BITS), + + FRAC_ONE = (1L << FRAC_BITS), // 1.0 + FRAC_HALF = (1L << (FRAC_BITS-1)) // 0.5 +}; + +/* + * Fixed-point fractions, used by the sound rate converter and other code. + */ +typedef gint32 frac_t; + +static inline frac_t double_to_frac(double value) { return (frac_t)(value * FRAC_ONE); } +static inline double frac_to_double(frac_t value) { return ((double)value) / FRAC_ONE; } + +static inline frac_t int_to_frac(gint32 value) { return value << FRAC_BITS; } +static inline gint32 frac_to_int(frac_t value) { return value >> FRAC_BITS; } + +#endif /* !SCI_TYPES */ diff --git a/engines/sci/include/script.h b/engines/sci/include/script.h new file mode 100644 index 0000000000..f979bd2a1e --- /dev/null +++ b/engines/sci/include/script.h @@ -0,0 +1,214 @@ +/*************************************************************************** + script.h Copyright (C) 2000,01 Magnus Reftel + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#ifndef SCRIPT_H +#define SCRIPT_H + +#include + +/*#define SCRIPT_DEBUG */ + +#define SCI_SCRIPTS_NR 1000 + +typedef struct script_opcode_ +{ + unsigned opcode; + int arg1, arg2, arg3; + int pos, size; +} script_opcode; + + +typedef enum { + sci_obj_terminator, + sci_obj_object, + sci_obj_code, + sci_obj_synonyms, + sci_obj_said, + sci_obj_strings, + sci_obj_class, + sci_obj_exports, + sci_obj_pointers, + sci_obj_preload_text, /* This is really just a flag. */ + sci_obj_localvars +} script_object_types; + +void script_dissect(resource_mgr_t *resmgr, int res_no, char **snames, int snames_nr); + +/* Opcode formats as used by script.c */ +typedef enum { + Script_Invalid=-1, + Script_None=0, + Script_Byte, + Script_SByte, + Script_Word, + Script_SWord, + Script_Variable, + Script_SVariable, + Script_SRelative, + Script_Property, + Script_Global, + Script_Local, + Script_Temp, + Script_Param, + Script_Offset, + Script_End +} opcode_format; + +typedef enum { /* FIXME */ + op_bnot = 0, + op_add, + op_sub, + op_mul, + op_div, + op_mod, + op_shr, + op_shl, + op_xor, + op_and, + op_or, + op_neg, + op_not, + op_eq, + op_ne_, + op_gt_, + op_ge_, + op_lt_, + op_le_, + op_ugt_, + op_uge_, + op_ult_, + op_ule_, + op_bt, + op_bnt, + op_jmp, + op_ldi, + op_push, + op_pushi, + op_toss, + op_dup, + op_link, + op_call = 0x20, + op_callk, + op_callb, + op_calle, + op_ret, + op_send, + op_class = 0x28, + op_self = 0x2a, + op_super, + op_rest, + op_lea, + op_selfID, + op_pprev = 0x30, + op_pToa, + op_aTop, + op_pTos, + op_sTop, + op_ipToa, + op_dpToa, + op_ipTos, + op_dpTos, + op_lofsa, + op_lofss, + op_push0, + op_push1, + op_push2, + op_pushSelf, + op_lag = 0x40, + op_lal, + op_lat, + op_lap, + op_lagi, + op_lali, + op_lati, + op_lapi, + op_lsg, + op_lsl, + op_lst, + op_lsp, + op_lsgi, + op_lsli, + op_lsti, + op_lspi, + op_sag, + op_sal, + op_sat, + op_sap, + op_sagi, + op_sali, + op_sati, + op_sapi, + op_ssg, + op_ssl, + op_sst, + op_ssp, + op_ssgi, + op_ssli, + op_ssti, + op_sspi, + op_plusag, + op_plusal, + op_plusat, + op_plusap, + op_plusagi, + op_plusali, + op_plusati, + op_plusapi, + op_plussg, + op_plussl, + op_plusst, + op_plussp, + op_plussgi, + op_plussli, + op_plussti, + op_plusspi, + op_minusag, + op_minusal, + op_minusat, + op_minusap, + op_minusagi, + op_minusali, + op_minusati, + op_minusapi, + op_minussg, + op_minussl, + op_minusst, + op_minussp, + op_minussgi, + op_minussli, + op_minussti, + op_minusspi +} sci_opcodes; + +extern DLLEXTERN opcode_format formats[128][4]; + +void script_adjust_opcode_formats(int res_version); + +int +script_find_selector(struct _state *s, const char *selector_name); +/* Determines the selector ID of a selector by its name +** Parameters: (state_t *) s: VM state +** (char *) selector_name: Name of the selector to look up +** Returns : (int) The appropriate selector ID, or -1 on error +*/ + +struct _state; +void script_free_breakpoints(struct _state *s); + +#endif diff --git a/engines/sci/include/seg_manager.h b/engines/sci/include/seg_manager.h new file mode 100644 index 0000000000..600792568e --- /dev/null +++ b/engines/sci/include/seg_manager.h @@ -0,0 +1,627 @@ +/*************************************************************************** + seg_manager.h Copyright (C) 2002 Xiaojun Chen, Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + + +#ifndef _SCI_SEG_MANAGER_H +#define _SCI_SEG_MANAGER_H + +#include +#include +#include + +#define DEFAULT_SCRIPTS 32 +#define DEFAULT_OBJECTS 8 /* default # of objects per script */ +#define DEFAULT_OBJECTS_INCREMENT 4 /* Number of additional objects to + ** instantiate if we're running out of them */ + +/* SCRIPT_ID must be 0 */ +typedef enum { + SCRIPT_ID, + SEG_ID +} id_flag; + +//void dbg_print( const char* msg, void *i ); /* for debug only */ + +/* verify the the given condition is true, output the message if condition is false, and exit +** Parameters: +** cond - condition to be verified +** msg - the message to be printed if condition fails +** return: +** none, terminate the program if fails +*/ +#define VERIFY( cond, msg ) if (! ( cond ) ) {\ + sciprintf( "%s, line, %d, %s\n", __FILE__, __LINE__, msg ); \ + BREAKPOINT(); \ + } + + +#define MEM_OBJ_INVALID 0 +#define MEM_OBJ_SCRIPT 1 +#define MEM_OBJ_CLONES 2 +#define MEM_OBJ_LOCALS 3 +#define MEM_OBJ_STACK 4 +#define MEM_OBJ_SYS_STRINGS 5 +#define MEM_OBJ_LISTS 6 +#define MEM_OBJ_NODES 7 +#define MEM_OBJ_HUNK 8 +#define MEM_OBJ_DYNMEM 9 +#define MEM_OBJ_RESERVED 10 +#define MEM_OBJ_MAX MEM_OBJ_RESERVED /* For sanity checking */ +typedef int mem_obj_enum; + +struct _mem_obj; + +#define GET_SEGMENT(mgr, index, rtype) ((index) > 0 && (mgr).heap_size > index)? \ + (((mgr).heap[index] && (mgr).heap[index]->type == rtype)? (mgr).heap[index] \ + : NULL) /* Type does not match */ \ + : NULL /* Invalid index */ + +#define GET_SEGMENT_ANY(mgr, index) ((index) > 0 && (mgr).heap_size > index)? \ + (((mgr).heap[index])? (mgr).heap[index] \ + : NULL) /* Type does not match */ \ + : NULL /* Invalid index */ + +#define GET_OBJECT_SEGMENT(mgr, index) ((index) > 0 && (mgr).heap_size > index)? \ + (((mgr).heap[index] \ + && ((mgr).heap[index]->type == MEM_OBJ_SCRIPT \ + || (mgr).heap[index]->type == MEM_OBJ_CLONES))? (mgr).heap[index] \ + : NULL) /* Type does not match */ \ + : NULL /* Invalid index */ + + +typedef struct _seg_manager_t { + int_hash_map_t* id_seg_map; /* id - script id; seg - index of heap */ + struct _mem_obj** heap; + int heap_size; /* size of the heap */ + int reserved_id; + int exports_wide; + int sci1_1; + + int gc_mark_bits; /* For standard Mark&Sweep: + ** 1 or 0, depending on what unreachable/freshly allocated + ** memory is tagged as */ + size_t mem_allocated; /* Total amount of memory allocated */ + + seg_id_t clones_seg_id; /* ID of the (a) clones segment */ + seg_id_t lists_seg_id; /* ID of the (a) list segment */ + seg_id_t nodes_seg_id; /* ID of the (a) node segment */ + seg_id_t hunks_seg_id; /* ID of the (a) hunk segment */ +} seg_manager_t; + + + +/*==============================================================*/ +/* Toplevel functionality */ +/*==============================================================*/ +void +sm_init (seg_manager_t* self, int sci1_1); +/* Initialize the segment manager +*/ + +void +sm_destroy (seg_manager_t* self); +/* Deallocate all memory associated with the segment manager +*/ + +void +sm_gc(seg_manager_t *self, struct _state *s); +/* Perform garbage collection +** Parameters: (state_t *) s: The state to operate on +** Effects : Unreachable objects in 's' are deallocated +*/ + + + +/*==============================================================*/ +/* 1. Scripts */ +/*==============================================================*/ + + +void +sm_free_script ( mem_obj_t* mem ); + +mem_obj_t* +sm_allocate_script(struct _seg_manager_t* self, struct _state *s, int script_nr, int* seg_id); +/* Allocate a script into the segment manager +** Parameters: (int) script_nr: number of the script to load +** (state_t *) s: The state containing resource manager handlers to load the +** script data +** Returns : (int) 0 on failure, 1 on success +** (int) *seg_id: The segment ID of the newly allocated segment, on success + +** The script must then be initialised; see section (1b.), below. +*/ + +int +sm_deallocate_script(struct _seg_manager_t* self, int script_nr); +/* Forcefully deallocate a previously allocated script +** Parameters: (int) script_nr: number of the script to deallocate +** Returns : (int) 1 on success, 0 on failure +*/ + +int +sm_script_is_loaded(struct _seg_manager_t* self, int id, id_flag flag); +/* Determines whether a script has been loaded yet +** Parameters: (int) id: number of the script or ID of the script segment to check for +** (id_flag) flag: Whether to address the script by script number (SCRIPT_ID) or +** by its segment (SEG_ID). SEG_ID is faster than SCRIPT_ID, +** but less convenient. +*/ + +guint16 +sm_validate_export_func(struct _seg_manager_t* self, int pubfunct, int seg); +/* Validate whether the specified public function is exported by the script in the specified segment +** Parameters: (int) pubfunct: Index of the function to validate +** (int) seg: Segment ID of the script the check is to be performed for +** Returns : (guint16) 0 if the public function is invalid, its offset into the script's segment +** otherwise +**/ + +int +sm_seg_get (seg_manager_t* self, int script_nr); +/* Get the segment ID associated with a script number +** Parameters: (int) script_nr: Number of the script to look up +** Returns : (int) The associated segment ID, or -1 if no matching segment exists +** This function is "pure" (i.e, it doesn't modify anything). +*/ + + +/**************************/ +/* script lock operations */ +/**************************/ + +void +sm_increment_lockers(struct _seg_manager_t* self, int id, id_flag flag); +/* Increments the number of lockers of the script in question by one +** Parameters: (int) id: ID of the script or script segment to modify +** (id_flag) flag: Whether to address the script by script number (SCRIPT_ID) or +** by its segment (SEG_ID). SEG_ID is faster than SCRIPT_ID, +** but less convenient. +*/ + +void +sm_decrement_lockers(struct _seg_manager_t* self, int id, id_flag flag); +/* Decrements the number of lockers of the script in question by one +** Parameters: (int) id: ID of the script or script segment to modify +** (id_flag) flag: Whether to address the script by script number (SCRIPT_ID) or +** by its segment (SEG_ID). SEG_ID is faster than SCRIPT_ID, +** but less convenient. +*/ + +int +sm_get_lockers(struct _seg_manager_t* self, int id, id_flag flag); +/* Retrieves the number of locks held on this script +** Parameters: (int) id: ID of the script or script segment to read from +** (id_flag) flag: Whether to address the script by script number (SCRIPT_ID) or +** by its segment (SEG_ID). SEG_ID is faster than SCRIPT_ID, +** but less convenient. +** Returns : (int) The number of locks held on the previously identified script +*/ + +void +sm_set_lockers(struct _seg_manager_t* self, int lockers, int id, id_flag flag); +/* Sets the number of locks held on the specified script +** Parameters: (int) id: ID of the script or script segment to modify +** (id_flag) flag: Whether to address the script by script number (SCRIPT_ID) or +** by its segment (SEG_ID). SEG_ID is faster than SCRIPT_ID, +** but less convenient. +*/ + +byte * +sm_get_synonyms (struct _seg_manager_t* self, int id, id_flag flag); +/* Retrieves a pointer to the synonyms associated with the specified script +** Parameters: (int) id: ID of the script or script segment to read from +** (id_flag) flag: Whether to address the script by script number (SCRIPT_ID) or +** by its segment (SEG_ID). SEG_ID is faster than SCRIPT_ID, +** but less convenient. +** Returns : (byte *) Pointer to the synonyms, in non-parsed format. +** A dynamic failure is issued if the specified ID does not reference a proper script. +*/ + +int +sm_get_synonyms_nr (struct _seg_manager_t* self, int id, id_flag flag); +/* Retrieves the number of synonyms associated with the specified script +** Parameters: (int) id: ID of the script or script segment to read from +** (id_flag) flag: Whether to address the script by script number (SCRIPT_ID) or +** by its segment (SEG_ID). SEG_ID is faster than SCRIPT_ID, +** but less convenient. +** Returns : (int) The number of synonyms associated with the specified script +** A dynamic failure is issued if the specified ID does not reference a proper script. +*/ + + +/*==============================================================*/ +/* 1b. Script Initialisation */ +/*==============================================================*/ + +/*******************************************/ +/* The set of functions below are intended */ +/* to be used during script instantiation, */ +/* i.e. loading and linking. */ +/*******************************************/ + +void +sm_script_initialise_locals_zero(struct _seg_manager_t *self, seg_id_t seg, int nr); +/* Initializes a script's local variable block +** Parameters: (seg_id_t) seg: Segment containing the script to initialize +** (int) nr: Number of local variables to allocate +** All variables are initialized to zero. +*/ + +void +sm_script_initialise_locals(struct _seg_manager_t *self, reg_t location); +/* Initializes a script's local variable block according to a prototype +** Parameters: (reg_t) location: Location to initialize from +*/ + +object_t * +sm_script_obj_init(seg_manager_t *self, struct _state *s, reg_t obj_pos); +/* Initializes an object within the segment manager +** Parameters: (reg_t) obj_pos: Location (segment, offset) of the object +** Returns : (object_t *) A newly created object_t describing the object +** obj_pos must point to the beginning of the script/class block (as opposed +** to what the VM considers to be the object location) +** The corresponding object_t is stored within the relevant script. +*/ + +void +sm_script_add_code_block(struct _seg_manager_t* self, reg_t location); +/* Informs the segment manager that a code block must be relocated +** Parameters: (reg_t) location: Start of block to relocate +*/ + +void +sm_set_export_width(struct _seg_manager_t* self, int flag); +/* Tells the segment manager whether exports are wide (32-bit) or not. +** Parameters: (int) flag: 1 if exports are wide, 0 otherwise */ + +void +sm_script_relocate(struct _seg_manager_t* self, reg_t block); +/* Processes a relocation block witin a script +** Parameters: (reg_t) obj_pos: Location (segment, offset) of the block +** Returns : (object_t *) Location of the relocation block +** This function is idempotent, but it must only be called after all +** objects have been instantiated, or a run-time error will occur. +*/ + +void +sm_script_free_unused_objects(struct _seg_manager_t *self, seg_id_t segid); +/* Deallocates all unused but allocated entries for objects +** Parameters: (seg_id_t) segid: segment of the script to prune in this way +** These entries are created during script instantiation; deallocating them +** frees up some additional memory. +*/ + +void +sm_set_export_table_offset (struct _seg_manager_t* self, int offset, int id, id_flag flag); +/* Sets the script-relative offset of the exports table +** Parameters: (int) offset: The script-relative exports table offset +** (int) id: ID of the script or script segment to write to +** (id_flag) flag: Whether to address the script by script number (SCRIPT_ID) or +** by its segment (SEG_ID). SEG_ID is faster than SCRIPT_ID, +** but less convenient. +** A dynamic failure is issued if the specified ID does not reference a proper script. +*/ + +void +sm_set_synonyms_offset (struct _seg_manager_t* self, int offset, int id, id_flag flag); +/* Sets the script-relative offset of the synonyms associated with the specified script +** Parameters: (int) offset: The script-relative offset of the synonyms block +** (int) id: ID of the script or script segment to write to +** (id_flag) flag: Whether to address the script by script number (SCRIPT_ID) or +** by its segment (SEG_ID). SEG_ID is faster than SCRIPT_ID, +** but less convenient. +** A dynamic failure is issued if the specified ID does not reference a proper script. +*/ + +void +sm_set_synonyms_nr (struct _seg_manager_t* self, int nr, int id, id_flag flag); +/* Sets the number of synonyms associated with the specified script +** Parameters: (int) nr: The number of synonyms, as to be stored within the script +** (int) id: ID of the script or script segment to write to +** (id_flag) flag: Whether to address the script by script number (SCRIPT_ID) or +** by its segment (SEG_ID). SEG_ID is faster than SCRIPT_ID, +** but less convenient. +** A dynamic failure is issued if the specified ID does not reference a proper script. +*/ + +void +sm_mark_script_deleted(seg_manager_t* self, int script_nr); +/* Marks the script identified by its script number as deleted +** Parameters: (int) script_nr: Script number to mark as deleted +** This will not actually delete the script. If references remain present on the +** heap or the stack, the script will stay in memory in a quasi-deleted state until +** either unreachable (resulting in its eventual deletion) or reloaded (resulting +** in its data being updated). +*/ + +void +sm_unmark_script_deleted(seg_manager_t* self, int script_nr); +/* Marks the script identified by its script number as not deleted +** Parameters: (int) script_nr: Script number to mark as not deleted +*/ + +int +sm_script_is_marked_as_deleted(seg_manager_t* self, seg_id_t seg); +/* Determines whether the script referenced by the indicated segment is marked as being deleted. +** Parameters: (seg_id_t) Segment ID of the script to investigate +** Returns : (int) 1 iff seg points to a script and the segment is deleted, 0 otherwise +** Will return 0 when applied to an invalid or non-script seg. +*/ + + + +/*==============================================================*/ +/* 2. Clones */ +/*==============================================================*/ + +clone_t* +sm_alloc_clone(struct _seg_manager_t *self, reg_t *addr); +/* Allocate a fresh clone +** Returns : (clone_t*): Reference to the memory allocated for the clone +** (reg_t) *addr: The offset of the freshly allocated clone +*/ + +void +sm_free_clone(struct _seg_manager_t *self, reg_t addr); +/* Deallocates a clone +** Parameters: (reg_t) addr: Offset of the clone scheduled for termination +*/ + + + +/*==============================================================*/ +/* Objects (static, from Scripts, and dynmic, from Clones) */ +/*==============================================================*/ + +/* Not all of these functions are fully operational for clones ATM */ + +gint16 +sm_get_heap(struct _seg_manager_t* self, reg_t reg ); +/* Retrieves a 16 bit value from within a script's heap representation +** Parameters: (reg_t) reg: The address to read from +** Returns : (gint16) The value read from the specified location +*/ + +void +sm_put_heap(struct _seg_manager_t* self, reg_t reg, gint16 value ); +/* Writes a 16 bit value into a script's heap representation +** Parameters: (reg_t) reg: The address to write to +** (gint16) value: The value to write +*/ + +void +sm_mcpy_in_out (seg_manager_t* self, int dst, const void* src, size_t n, int id, int flag); +/* Copies a byte string into a script's heap representation +** Parameters: (int) dst: The script-relative offset of the destination area +** (const void *) src: Pointer to the data source location +** (size_t) n: Number of bytes to copy +** (int) id: ID of the script or script segment to write to +** (id_flag) flag: Whether to address the script by script number (SCRIPT_ID) or +** by its segment (SEG_ID). SEG_ID is faster than SCRIPT_ID, +** but less convenient. +** A dynamic failure is issued if the specified ID does not reference a proper script. +*/ + + +/*==============================================================*/ +/* 4. Stack */ +/*==============================================================*/ + +dstack_t * +sm_allocate_stack(struct _seg_manager_t *self, int size, seg_id_t *segid); +/* Allocates a data stack +** Parameters: (int) size: Number of stack entries to reserve +** Returns : (dstack_t *): The physical stack +** (seg_id_t) segid: Segment ID of the stack +*/ + + + +/*==============================================================*/ +/* 5. System Strings */ +/*==============================================================*/ + +sys_strings_t * +sm_allocate_sys_strings(struct _seg_manager_t *self, seg_id_t *segid); +/* Allocates a system string table +** Returns : (dstack_t *): The physical stack +** (seg_id_t) segid: Segment ID of the stack +** See also sys_string_acquire(); +*/ + + + +/*==============================================================*/ +/* 6, 7. Lists and Nodes */ +/*==============================================================*/ + +list_t* +sm_alloc_list(struct _seg_manager_t *self, reg_t *addr); +/* Allocate a fresh list +** Returns : (listY_t*): Reference to the memory allocated for the list +** (reg_t) *addr: The offset of the freshly allocated list +*/ + +void +sm_free_list(struct _seg_manager_t *self, reg_t addr); +/* Deallocates a list +** Parameters: (reg_t) addr: Offset of the list scheduled for termination +*/ + + +node_t* +sm_alloc_node(struct _seg_manager_t *self, reg_t *addr); +/* Allocate a fresh node +** Returns : (node_t*): Reference to the memory allocated for the node +** (reg_t) *addr: The offset of the freshly allocated node +*/ + +void +sm_free_node(struct _seg_manager_t *self, reg_t addr); +/* Deallocates a list node +** Parameters: (reg_t) addr: Offset of the node scheduled for termination +*/ + + + +/*==============================================================*/ +/* 8. Hunk Memory */ +/*==============================================================*/ + +hunk_t* +sm_alloc_hunk_entry(struct _seg_manager_t *self, const char *hunk_type, int size, reg_t *addr); +/* Allocate a fresh chunk of the hunk +** Parameters: (int) size: Number of bytes to allocate for the hunk entry +** (const char *) hunk_type: A descriptive string for the hunk entry, +** for debugging purposes +** Returns : (hunk_t*): Reference to the memory allocated for the hunk piece +** (reg_t) *addr: The offset of the freshly allocated hunk entry +*/ + +void +sm_free_hunk_entry(struct _seg_manager_t *self, reg_t addr); +/* Deallocates a hunk eentry +** Parameters: (reg_t) addr: Offset of the hunk entry to delete +*/ + + + +/*==============================================================*/ +/* 9. Dynamic Memory */ +/*==============================================================*/ + +unsigned char * +sm_alloc_dynmem(struct _seg_manager_t *self, int size, const char *description, reg_t *addr); +/* Allocate some dynamic memory +** Parameters: (int) size: Number of bytes to allocate +** (const char_ *) description: A descriptive string, +** for debugging purposes +** Returns : (unsigned char*): Raw pointer into the allocated dynamic memory +** (reg_t) *addr: The offset of the freshly allocated X +*/ + +int +sm_free_dynmem(struct _seg_manager_t *self, reg_t addr); +/* Deallocates a piece of dynamic memory +** Parameters: (reg_t) addr: Offset of the dynmem chunk to free +*/ + +const char * +sm_get_description(struct _seg_manager_t *self, reg_t addr); +/* Gets the description of a dynmem segment +** Parameters: (reg_t) addr: Segment to describe +** Returns : (const char *): Pointer to the descriptive string set in +** sm_alloc_dynmem +*/ + +/*==============================================================*/ +/* 10. Reserved segments */ +/*==============================================================*/ + +seg_id_t +sm_allocate_reserved_segment(struct _seg_manager_t *self, char *name); +/* Reserves a special-purpose segment +** Parameters: (char *) name: A string name identifying the segment (the string is cloned and retained) +** Returns : A fresh segment ID for the segment in question +** Reserved segments are never used by the segment manager. They can be used to tag special-purpose addresses. +** Segment 0 is implicitly reserved for numbers. +*/ + +/*==============================================================*/ +/* Generic Operations on Segments and Addresses */ +/*==============================================================*/ + +byte * +sm_dereference(struct _seg_manager_t *self, reg_t reg, int *size); +/* Dereferences a raw memory pointer +** Parameters: (reg_t) reg: The reference to dereference +** Returns : (byte *) The data block referenced +** (int) size: (optionally) the theoretical maximum size of it +*/ + + +/*==============================================================*/ +/* 11. Segment interface, primarily for GC */ +/*==============================================================*/ + +typedef struct _seg_interface { + seg_manager_t *segmgr; + mem_obj_t *mobj; + seg_id_t seg_id; + mem_obj_enum type_id; /* Segment type */ + const char *type; /* String description of the segment type */ + + reg_t + (*find_canonic_address)(struct _seg_interface *self, reg_t sub_addr); + /* Finds the canonic address associated with sub_reg + ** Parameters: (reg_t) sub_addr: The base address whose canonic address is to be found + ** For each valid address a, there exists a canonic address c(a) such that c(a) = c(c(a)). + ** This address "governs" a in the sense that deallocating c(a) will deallocate a. + */ + + void + (*free_at_address)(struct _seg_interface *self, reg_t sub_addr); + /* Deallocates all memory associated with the specified address + ** Parameters: (reg_t) sub_addr: The address (within the given segment) to deallocate + */ + + void + (*list_all_deallocatable)(struct _seg_interface *self, void *param, void (*note)(void *param, reg_t addr)); + /* Iterates over and reports all addresses within the current segment + ** Parameters: note : (voidptr * addr) -> (): Invoked for each address on which free_at_address() + ** makes sense + ** (void *) param: Parameter passed to 'note' + */ + + void + (*list_all_outgoing_references)(struct _seg_interface *self, struct _state *s, reg_t object, void *param, void (*note)(void *param, reg_t addr)); + /* Iterates over all references reachable from the specified object + ** Parameters: (reg_t) object: The object (within the current segment) to analyse + ** (void *) param: Parameter passed to 'note' + ** note : (voidptr * addr) -> (): Invoked for each outgoing reference within the object + ** Note: This function may also choose to report numbers (segment 0) as adresses + */ + + void + (*deallocate_self)(struct _seg_interface *self); + /* Deallocates the segment interface + */ + +} seg_interface_t; + +seg_interface_t * +get_seg_interface(seg_manager_t *self, seg_id_t segid); +/* Retrieves the segment interface to the specified segment +** Parameters: (seg_id_t) segid: ID of the segment to look up +** Returns : (seg_interface_t *): An interface to the specified segment ID, or NULL on error +** The returned interface 'si' must be freed after use by calling 'si->dealloc_self(si)'; +*/ + + + +#endif diff --git a/engines/sci/include/sfx_core.h b/engines/sci/include/sfx_core.h new file mode 100644 index 0000000000..9a04e9f9e5 --- /dev/null +++ b/engines/sci/include/sfx_core.h @@ -0,0 +1,40 @@ +/*************************************************************************** + sfx_core.h Copyright (C) 2003,04 Christoph Reichenbach + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + + Please contact the maintainer for any program-related bug reports or + inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef _SFX_CORE_H_ +#define _SFX_CORE_H_ + +#include + +#define SFX_OK 0 +#define SFX_ERROR -1 + +#define MIDI_CHANNELS 16 + +#endif /* !defined(_SFX_CORE_H_) */ diff --git a/engines/sci/include/sfx_engine.h b/engines/sci/include/sfx_engine.h new file mode 100644 index 0000000000..950f1e3dc8 --- /dev/null +++ b/engines/sci/include/sfx_engine.h @@ -0,0 +1,179 @@ +/*************************************************************************** + sfx_engine.h Copyright (C) 2003,04 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Sound engine */ +#ifndef _SFX_ENGINE_H_ +#define _SFX_ENGINE_H_ + +#include +#include +#include +#include + +#define SOUND_TICK 1000000 / 60 +/* Approximately 16666 microseconds */ + + +#define SFX_STATE_FLAG_MULTIPLAY (1 << 0) /* More than one song playable + ** simultaneously ? */ +#define SFX_STATE_FLAG_NOSOUND (1 << 1) /* Completely disable sound playing */ + + +#define SFX_DEBUG_SONGS (1 << 0) /* Debug song changes */ +#define SFX_DEBUG_CUES (1 << 1) /* Debug cues, loops, and + ** song completions */ + +typedef struct { + song_iterator_t *it; /* The song iterator at the heart of things */ + unsigned int flags; /* SFX_STATE_FLAG_* */ + songlib_t songlib; /* Song library */ + song_t *song; /* Active song, or start of active song chain */ + int suspended; /* Whether we are suspended */ + unsigned int debug; /* Debug flags */ + +} sfx_state_t; + +/***********/ +/* General */ +/***********/ + +void +sfx_init(sfx_state_t *self, resource_mgr_t *resmgr, int flags); +/* Initializes the sound engine +** Parameters: (resource_mgr_t *) resmgr: Resource manager for initialization +** (int) flags: SFX_STATE_FLAG_* +*/ + +void +sfx_exit(sfx_state_t *self); +/* Deinitializes the sound subsystem +*/ + +void +sfx_suspend(sfx_state_t *self, int suspend); +/* Suspends/unsuspends the sound sybsystem +** Parameters: (int) suspend: Whether to suspend (non-null) or to unsuspend +*/ + +int +sfx_poll(sfx_state_t *self, song_handle_t *handle, int *cue); +/* Polls the sound server for cues etc. +** Returns : (int) 0 if the cue queue is empty, SI_LOOP, SI_CUE, or SI_FINISHED otherwise +** (song_handle_t) *handle: The affected handle +** (int) *cue: The sound cue number (if SI_CUE), or the loop number (if SI_LOOP) +*/ + +int +sfx_poll_specific(sfx_state_t *self, song_handle_t handle, int *cue); +/* Polls the sound server for cues etc. +** Parameters: (song_handle_t) handle: The handle to poll +** Returns : (int) 0 if the cue queue is empty, SI_LOOP, SI_CUE, or SI_FINISHED otherwise +** (int) *cue: The sound cue number (if SI_CUE), or the loop number (if SI_LOOP) +*/ + +int +sfx_get_volume(sfx_state_t *self); +/* Determines the current global volume settings +** Returns : (int) The global volume, between 0 (silent) and 127 (max. volume) +*/ + +void +sfx_set_volume(sfx_state_t *self, int volume); +/* Determines the current global volume settings +** Parameters: (int) volume: The new global volume, between 0 and 127 (see above) +*/ + +void +sfx_all_stop(sfx_state_t *self); +/* Stops all songs currently playing, purges song library +*/ + + +/*****************/ +/* Song basics */ +/*****************/ + +int +sfx_add_song(sfx_state_t *self, song_iterator_t *it, int priority, song_handle_t handle, int resnum); +/* Adds a song to the internal sound library +** Parameters: (song_iterator_t *) it: The iterator describing the song +** (int) priority: Initial song priority (higher <-> more important) +** (song_handle_t) handle: The handle to associate with the song +** Returns : (int) 0 on success, nonzero on error +*/ + + +void +sfx_remove_song(sfx_state_t *self, song_handle_t handle); +/* Deletes a song and its associated song iterator from the song queue +** Parameters: (song_handle_t) handle: The song to remove +*/ + + +/**********************/ +/* Song modifications */ +/**********************/ + + +void +sfx_song_set_status(sfx_state_t *self, song_handle_t handle, int status); +/* Sets the song status, i.e. whether it is playing, suspended, or stopped. +** Parameters: (song_handle_t) handle: Handle of the song to modify +** (int) status: The song status the song should assume +** WAITING and PLAYING are set implicitly and essentially describe the same state +** as far as this function is concerned. +*/ + +void +sfx_song_renice(sfx_state_t *self, song_handle_t handle, int priority); +/* Sets the new song priority +** Parameters: (song_handle_t) handle: The handle to modify +** (int) priority: The priority to set +*/ + +void +sfx_song_set_loops(sfx_state_t *self, song_handle_t handle, int loops); +/* Sets the number of loops for the specified song +** Parameters: (song_handle_t) handle: The song handle to reference +** (int) loops: Number of loops to set +*/ + +void +sfx_song_set_hold(sfx_state_t *self, song_handle_t handle, int hold); +/* Sets the number of loops for the specified song +** Parameters: (song_handle_t) handle: The song handle to reference +** (int) hold: Number of loops to setn +*/ + +void +sfx_song_set_fade(sfx_state_t *self, song_handle_t handle, fade_params_t *fade_setup); +/* Instructs a song to be faded out +** Parameters: (song_handle_t) handle: The song handle to reference +** (fade_params_t *) fade_setup: The precise fade-out configuration to use +*/ + + +#endif /* !defined(_SFX_ENGINE_H_) */ diff --git a/engines/sci/include/sfx_iterator.h b/engines/sci/include/sfx_iterator.h new file mode 100644 index 0000000000..de357b053d --- /dev/null +++ b/engines/sci/include/sfx_iterator.h @@ -0,0 +1,353 @@ +/*************************************************************************** + sfx_iterator.h (C) 2002..04 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [reichenb@colorado.edu] + +***************************************************************************/ +/* Song iterator declarations */ + +#ifndef _SCI_SFX_ITERATOR_H_ +#define _SCI_SFX_ITERATOR_H_ + +#include +#include + +#define SI_FINISHED -1 /* Song finished playing */ +#define SI_LOOP -2 /* Song just looped */ +#define SI_ABSOLUTE_CUE -3 /* Found a song cue (absolute) */ +#define SI_RELATIVE_CUE -4 /* Found a song cue (relative) */ +#define SI_PCM -5 /* Found a PCM */ +#define SI_IGNORE -6 /* This event got edited out by the remapper */ +#define SI_MORPH -255 /* Song iterator requested self-morph. */ + +#define FADE_ACTION_NONE 0 +#define FADE_ACTION_FADE_AND_STOP 1 +#define FADE_ACTION_FADE_AND_CONT 2 + +typedef struct { + int ticks_per_step; + int final_volume; + int step_size; + int action; +} fade_params_t; + +#define SONG_ITERATOR_MESSAGE_ARGUMENTS_NR 2 + +/* Helper defs for messages */ +/* Base messages */ +#define _SIMSG_BASE 0 /* Any base decoder */ +#define _SIMSG_BASEMSG_SET_LOOPS 0 /* Set loops */ +#define _SIMSG_BASEMSG_CLONE 1 /* Clone object and data. Must provide the + ** (possibly negative) number of ticks that have + ** passed since the last delay time started being + ** used */ +#define _SIMSG_BASEMSG_SET_PLAYMASK 2 /* Set the current playmask for filtering */ +#define _SIMSG_BASEMSG_SET_RHYTHM 3 /* Activate/deactivate rhythm channel */ +#define _SIMSG_BASEMSG_ACK_MORPH 4 /* Acknowledge self-morph */ +#define _SIMSG_BASEMSG_STOP 5 /* Stop iterator */ +#define _SIMSG_BASEMSG_PRINT 6 /* Print self to stderr, after printing param1 tabs */ +#define _SIMSG_BASEMSG_SET_HOLD 7 /* Set value of hold parameter to expect */ +#define _SIMSG_BASEMSG_SET_FADE 8 /* Set fade parameters */ + +/* "Plastic" (discardable) wrapper messages */ +#define _SIMSG_PLASTICWRAP 1 /* Any base decoder */ +#define _SIMSG_PLASTICWRAP_ACK_MORPH 4 /* Acknowledge self-morph */ + +/* Messages */ +#define SIMSG_SET_LOOPS(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_LOOPS,(x),0 +#define SIMSG_SET_PLAYMASK(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_PLAYMASK,(x),0 +#define SIMSG_SET_RHYTHM(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_RHYTHM,(x),0 +#define SIMSG_CLONE(x) _SIMSG_BASE,_SIMSG_BASEMSG_CLONE,(x),0 +#define SIMSG_ACK_MORPH _SIMSG_PLASTICWRAP,_SIMSG_PLASTICWRAP_ACK_MORPH,0,0 +#define SIMSG_STOP _SIMSG_BASE,_SIMSG_BASEMSG_STOP,0,0 +#define SIMSG_PRINT(indentation) _SIMSG_BASE,_SIMSG_BASEMSG_PRINT,(indentation),0 +#define SIMSG_SET_HOLD(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_HOLD,(x),0 +/*#define SIMSG_SET_FADE(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_FADE,(x),0*/ + +/* Message transmission macro: Takes song reference, message reference */ +#define SIMSG_SEND(o, m) songit_handle_message(&(o), songit_make_message((o)->ID, m)) +#define SIMSG_SEND_FADE(o, m) songit_handle_message(&(o), songit_make_ptr_message((o)->ID, _SIMSG_BASE, _SIMSG_BASEMSG_SET_FADE, m, 0)) + + +typedef unsigned long songit_id_t; + +typedef struct { + songit_id_t ID; + unsigned int recipient; /* Type of iterator supposed to receive this */ + unsigned int type; + union { + unsigned int i; + void * p; + } args[SONG_ITERATOR_MESSAGE_ARGUMENTS_NR]; +} song_iterator_message_t; + +#define INHERITS_SONG_ITERATOR \ + songit_id_t ID; \ + guint16 channel_mask; \ + fade_params_t fade; \ + unsigned int flags; \ + int priority; \ + int (*next) (song_iterator_t *self, unsigned char *buf, int *buf_size); \ + sfx_pcm_feed_t * (*get_pcm_feed) (song_iterator_t *s); \ + song_iterator_t * (* handle_message)(song_iterator_t *self, song_iterator_message_t msg); \ + void (*init) (struct _song_iterator *self); \ + void (*cleanup) (struct _song_iterator *self); \ + int (*get_timepos) (struct _song_iterator *self); \ + listener_t death_listeners[SONGIT_MAX_LISTENERS]; \ + int death_listeners_nr \ + +#define SONGIT_MAX_LISTENERS 2 + +typedef struct _song_iterator { + + songit_id_t ID; + guint16 channel_mask; /* Bitmask of all channels this iterator will use */ + fade_params_t fade; + unsigned int flags; + int priority; + + int (*next) (struct _song_iterator *self, + unsigned char *buf, int *result); + /* Reads the next MIDI operation _or_ delta time + ** Parameters: (song_iterator_t *) self + ** (byte *) buf: The buffer to write to (needs to be able to + ** store at least 4 bytes) + ** Returns : (int) zero if a MIDI operation was written, SI_FINISHED + ** if the song has finished playing, SI_LOOP if looping + ** (after updating the loop variable), SI_CUE if we found + ** a cue, SI_PCM if a PCM was found, or the number of ticks + ** to wait before this function should be called next. + ** (int) *result: Number of bytes written to the buffer + ** (equals the number of bytes that need to be passed + ** to the lower layers) for 0, the cue value for SI_CUE, + ** or the number of loops remaining for SI_LOOP. + ** If SI_PCM is returned, get_pcm() may be used to retrieve the associated + ** PCM, but this must be done before any subsequent calls to next(). + */ + + sfx_pcm_feed_t * (*get_pcm_feed) (struct _song_iterator *self); + /* Checks for the presence of a pcm sample + ** Parameters: (song_iterator_t *) self + ** Returns : (sfx_pcm_feed_t *) NULL if no PCM data was found, a + ** PCM feed otherwise + */ + + + struct _song_iterator * + (* handle_message)(struct _song_iterator *self, song_iterator_message_t msg); + /* Handles a message to the song iterator + ** Parameters: (song_iterator_t *) self + ** (song_iterator_messag_t) msg: The message to handle + ** Returns : (song_iterator_t *) NULL if the message was not understood, + ** self if the message could be handled, or a new song iterator + ** if the current iterator had to be morphed (but the message could + ** still be handled) + ** This function is not supposed to be called directly; use + ** songit_handle_message() instead. It should not recurse, since songit_handle_message() + ** takes care of that and makes sure that its delegate received the message (and + ** was morphed) before self. + */ + + + void (*init) (struct _song_iterator *self); + /* Resets/initializes the sound iterator + ** Parameters: (song_iterator_t *) self + ** Returns : (void) + */ + + void (*cleanup) (struct _song_iterator *self); + /* Frees any content of the iterator structure + ** Parameters: (song_iterator_t *) self + ** Does not physically free(self) yet. May be NULL if nothing needs to be done. + ** Must not recurse on its delegate. + */ + + int (*get_timepos) (struct _song_iterator *self); + /* Gets the song position to store in a savegame + ** Parameters: (song_iterator_t *) self + */ + + /* Death listeners */ + /* These are not reset during initialisation */ + listener_t death_listeners[SONGIT_MAX_LISTENERS]; + int death_listeners_nr; + + /* See songit_* for the constructor and non-virtual member functions */ + +} song_iterator_t; + + +/* Song iterator flags */ +#define SONGIT_FLAG_CLONE (1 << 0) /* This flag is set for clones, which are exclusively used in song players. + ** Thus, this flag distinguishes song iterators in the main thread from those + ** in the song-player thread. */ + +void +song_iterator_add_death_listener(song_iterator_t *it, + void *client, + void (*notify) (void *self, void *notifier)); +/* Adds a death listener to a song iterator +** Parameters: (song_iterator_t *) it: The iterator to add to +** (void *) client: The object wanting to be notified +** (void* x void* -> void) notify: The notification function +** to invoke +** Effects: Fatally terminates the program if no listener slots are +** available +** Death listeners are NOT cloned. +*/ + +void +song_iterator_remove_death_listener(song_iterator_t *it, + void *client); +/* Removes a death listener from a song iterator +** Parameters: (song_iterator_t *) it: The iterator to modify +** (void *) client: The object no longer wanting to be notified +** Effects: Fatally terminates the program if the listener was not +** found +** Death listeners are NOT cloned. +*/ + +/********************************/ +/*-- Song iterator operations --*/ +/********************************/ + +#define SCI_SONG_ITERATOR_TYPE_SCI0 0 +#define SCI_SONG_ITERATOR_TYPE_SCI1 1 + +#define IT_READER_MASK_MIDI (1 << 0) +#define IT_READER_MASK_DELAY (1 << 1) +#define IT_READER_MASK_LOOP (1 << 2) +#define IT_READER_MASK_CUE (1 << 3) +#define IT_READER_MASK_PCM (1 << 4) +#define IT_READER_MAY_FREE (1 << 10) /* Free SI_FINISHED iterators */ +#define IT_READER_MAY_CLEAN (1 << 11) + /* MAY_CLEAN: May instantiate cleanup iterators + ** (use for players; this closes open channels at the end of a song) */ + +#define IT_READER_MASK_ALL ( IT_READER_MASK_MIDI \ + | IT_READER_MASK_DELAY \ + | IT_READER_MASK_LOOP \ + | IT_READER_MASK_CUE \ + | IT_READER_MASK_PCM ) + +int +songit_next(song_iterator_t **it, unsigned char *buf, int *result, int mask); +/* Convenience wrapper around it->next +** Parameters: (song_iterator_t **it) Reference to the iterator to access +** (byte *) buf: The buffer to write to (needs to be able to +** store at least 4 bytes) +** (int) mask: IT_READER_MASK options specifying the events to +** listen for +** Returns : (int) zero if a MIDI operation was written, SI_FINISHED +** if the song has finished playing, SI_LOOP if looping +** (after updating the loop variable), SI_CUE if we found +** a cue, SI_PCM if a PCM was found, or the number of ticks +** to wait before this function should be called next. +** (int) *result: Number of bytes written to the buffer +** (equals the number of bytes that need to be passed +** to the lower layers) for 0, the cue value for SI_CUE, +** or the number of loops remaining for SI_LOOP. +*/ + +song_iterator_t * +songit_new(unsigned char *data, unsigned int size, int type, songit_id_t id); +/* Constructs a new song iterator object +** Parameters: (byte *) data: The song data to iterate over +** (unsigned int) size: Number of bytes in the song +** (int) type: One of the SCI_SONG_ITERATOR_TYPEs +** (songit_id_t) id: An ID for addressing the song iterator +** Returns : (song_iterator_t *) A newly allocated but uninitialized song +** iterator, or NULL if 'type' was invalid or unsupported +*/ + +song_iterator_t * +songit_new_tee(song_iterator_t *left, song_iterator_t *right, int may_destroy); +/* Combines two iterators, returns the next event available from either +** Parameters: (song_iterator_t *) left: One of the iterators +** (song_iterator_t *) right: The other iterator +** (int) may_destroy: Whether completed song iterators may be +** destroyed +** Returns : (song_iterator_t *) A combined iterator, as suggested above +*/ + + +void +songit_free(song_iterator_t *it); +/* Frees a song iterator and the song it wraps +** Parameters: (song_iterator_t *) it: The song iterator to free +** Returns : (void) +*/ + +song_iterator_message_t +songit_make_message(songit_id_t id, + int recipient_class, int type, int a1, int a2); +/* Create a song iterator message +** Parameters: (songit_id_t) id: song ID the message is targetted to +** (int) recipient_class: Message recipient class +** (int) type: Message type +** (int x int) a1, a2: Arguments +** You should only use this with the SIMSG_* macros +*/ + +song_iterator_message_t +songit_make_ptr_message(songit_id_t id, + int recipient_class, int type, void * a1, int a2); +/* Create a song iterator message, wherein the first parameter is a pointer +** Parameters: (songit_id_t) id: song ID the message is targetted to +** (int) recipient_class: Message recipient class +** (int) type: Message type +** (void* x int) a1, a2: Arguments +** You should only use this with the SIMSG_* macros +*/ + +int +songit_handle_message(song_iterator_t **it_reg, song_iterator_message_t msg); +/* Handles a message to the song iterator +** Parameters: (song_iterator_t **): A reference to the variable storing the song iterator +** Returns : (int) Non-zero if the message was understood +** The song iterator may polymorph as result of msg, so a writeable reference is required. +*/ + + +song_iterator_t * +songit_clone(song_iterator_t *it, int delta); +/* Clones a song iterator +** Parameters: (song_iterator_t *) it: The iterator to clone +** (int) delta: Number of ticks that still need to elapse until +** the next item should be read from the song iterator +** Returns : (song_iterator_t *) A shallow clone of 'it'. +** This performs a clone on the bottom-most part (containing the actual song data) _only_. +** The justification for requiring 'delta' to be passed in here is that this +** is typically maintained outside of the song iterator. +*/ + + +int +sfx_play_iterator_pcm(song_iterator_t *it, unsigned long handle); +/* Plays a song iterator that found a PCM through a PCM device, if possible +** Parameters: (song_iterator_t *) it: The iterator to play +** (song_handle_t) handle: Debug handle +** Returns : (int) 0 if the effect will not be played, nonzero if it will +** This assumes that the last call to 'it->next()' returned SI_PCM. +*/ + +#endif diff --git a/engines/sci/include/sfx_iterator_internal.h b/engines/sci/include/sfx_iterator_internal.h new file mode 100644 index 0000000000..02b32502ac --- /dev/null +++ b/engines/sci/include/sfx_iterator_internal.h @@ -0,0 +1,238 @@ +/*************************************************************************** + sfx_iterator_internal.h Copyright (C) 2003 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef _SFX_ITERATOR_INTERNAL_ +#define _SFX_ITERATOR_INTERNAL_ + +#include +#include + + +/* States */ + +#define SI_STATE_UNINITIALISED -1 +#define SI_STATE_DELTA_TIME 0 /* Now at a delta time */ +#define SI_STATE_COMMAND 1 /* Now at a MIDI operation */ +#define SI_STATE_PENDING 2 /* Pending for loop */ +#define SI_STATE_FINISHED 3 /* End of song */ +#define SI_STATE_PCM 4 /* Should report a PCM next (-> DELTA_TIME) */ +#define SI_STATE_PCM_MAGIC_DELTA 5 /* Should report a ``magic'' one tick delta time next (goes on to FINISHED) */ + + +/* Iterator types */ + +#define SCI_SONG_ITERATOR_TYPE_SCI0 0 +#define SCI_SONG_ITERATOR_TYPE_SCI1 1 + +#define SIPFX __FILE__" : " + + +typedef struct { + int state; /* SI_STATE_* */ + int offset; /* Offset into the data chunk */ + int end; /* Last allowed byte in track */ + int id; /* Some channel ID */ + int loop_offset; + int delay; /* Number of ticks before the + ** specified channel is next + ** used, or + ** CHANNEL_DELAY_MISSING to + ** indicate that the delay has + ** not yet been read */ + + /* Two additional offsets for recovering: */ + int initial_offset; + int playmask; /* Active playmask (MIDI channels to play in here) */ + int notes_played; /* #of notes played since the last loop start */ + int loop_timepos; /* Total delay for this channel's loop marker */ + int total_timepos; /* Number of ticks since the beginning, ignoring loops */ + int timepos_increment; /* Number of ticks until the next command (to add) */ + + int saw_notes; /* Bitmask of channels we have currently played notes on */ + byte last_cmd; /* Last operation executed, for running status */ +} song_iterator_channel_t; + +#define INHERITS_BASE_SONG_ITERATOR \ + INHERITS_SONG_ITERATOR; /* aka "extends song iterator" */ \ + \ + int polyphony[MIDI_CHANNELS]; /* # of simultaneous notes on each */ \ + int importance[MIDI_CHANNELS]; /* priority rating for each channel, 0 means unrated. */ \ + \ + \ + int ccc; /* Cumulative cue counter, for those who need it */ \ + unsigned char resetflag; /* for 0x4C -- on DoSound StopSound, do we return to start? */ \ + int device_id; /* ID of the device we generating events for */ \ + int active_channels; /* Number of active channels */ \ + unsigned int size; /* Song size */ \ + unsigned char *data; \ + \ + int loops; /* Number of loops remaining */ \ + int recover_delay + +typedef struct _base_song_iterator { + INHERITS_BASE_SONG_ITERATOR; +} base_song_iterator_t; + +/********************************/ +/*--------- SCI 0 --------------*/ +/********************************/ + +typedef struct { + INHERITS_BASE_SONG_ITERATOR; + song_iterator_channel_t channel; + int delay_remaining; /* Number of ticks that haven't been polled yet */ +} sci0_song_iterator_t; + + +/********************************/ +/*--------- SCI 1 --------------*/ +/********************************/ + + +typedef struct _sci1_sample { + int delta; /* Time left-- initially, this is 'Sample point 1'. + ** After initialisation, it is 'sample point 1 minus the sample point of the previous sample' */ + int size; + int announced; /* Announced for download (SI_PCM) */ + sfx_pcm_config_t format; + byte *data; + struct _sci1_sample *next; +} sci1_sample_t; + +typedef struct { + INHERITS_BASE_SONG_ITERATOR; + song_iterator_channel_t channels[MIDI_CHANNELS]; + + /* Invariant: Whenever channels[i].delay == CHANNEL_DELAY_MISSING, + ** channel_offset[i] points to a delta time object. */ + + int initialised; /* Whether the MIDI channel setup has been initialised */ + int channels_nr; /* Number of channels actually used */ + sci1_sample_t *next_sample; + int channels_looped; /* Number of channels that are ready to loop */ + + int delay_remaining; /* Number of ticks that haven't been polled yet */ + int hold; \ +} sci1_song_iterator_t; + +#define PLAYMASK_NONE 0x0 + +/*********************************/ +/*---------- Cleanup ------------*/ +/*********************************/ + + +song_iterator_t * +new_cleanup_iterator(unsigned int channels); +/* Creates a new song iterator with the purpose of sending notes-off channel commands +** Parameters: (unsigned int) channels: Channel mask to send these commands for +** Returns : A song iterator with the aforementioned purpose +*/ + +int +is_cleanup_iterator(song_iterator_t *it); +/* Determines whether a given song iterator is a cleanup song iterator +** Parameters: (song_iterator_t *) it: The iterator to check +** Returns : (int) 1 iff 'it' is a cleanup song iterator +** No deep recursion/delegation is considered. +*/ + + +/**********************************/ +/*--------- Fast Forward ---------*/ +/**********************************/ + +typedef struct { + INHERITS_SONG_ITERATOR; + song_iterator_t *delegate; + int delta; /* Remaining time */ +} fast_forward_song_iterator_t; + + +song_iterator_t * +new_fast_forward_iterator(song_iterator_t *it, int delta); +/* Creates a new song iterator which fast-forwards +** Parameters: (song_iterator_t *) it: The iterator to wrap +** (int) delta: The number of ticks to skip +** Returns : (song_iterator_t) A newly created song iterator +** which skips all delta times +** until 'delta' has been used up +*/ + +/**********************************/ +/*--------- Fast Forward ---------*/ +/**********************************/ + +#define MAX_BUF_SIZE 4 + +#define TEE_LEFT 0 +#define TEE_RIGHT 1 +#define TEE_LEFT_ACTIVE (1<<0) +#define TEE_RIGHT_ACTIVE (1<<1) +#define TEE_LEFT_READY (1<<2) /* left result is ready */ +#define TEE_RIGHT_READY (1<<3) /* right result is ready */ +#define TEE_LEFT_PCM (1<<4) +#define TEE_RIGHT_PCM (1<<5) + +#define TEE_MORPH_NONE 0 /* Not waiting to self-morph */ +#define TEE_MORPH_READY 1 /* Ready to self-morph */ + +typedef struct { + INHERITS_SONG_ITERATOR; + + int status; + + int may_destroy; /* May destroy song iterators */ + + int morph_deferred; /* One of TEE_MORPH_* above */ + + struct { + song_iterator_t *it; + byte buf[MAX_BUF_SIZE]; + int result; + int retval; + + byte channel_remap[MIDI_CHANNELS]; + /* Remapping for channels */ + + } children[2]; +} tee_song_iterator_t; + + +sfx_pcm_feed_t * +sfx_iterator_make_feed(byte *base_data, int offset, + int size, + sfx_pcm_config_t conf); +/* Generates a feed for a song iterator +** Parameters: (byte *) base_data: A refcounted memory chunk containing +** (among other things) PCM data +** (int) offset; Offset into base_data +** (int) size: Number of bytes to consider +** (pcm_data_internal_t) conf: PCM encoding +*/ + +#endif /* !defined(_SFX_ITERATOR_INTERNAL_ */ diff --git a/engines/sci/include/sfx_pcm.h b/engines/sci/include/sfx_pcm.h new file mode 100644 index 0000000000..b3a44868a9 --- /dev/null +++ b/engines/sci/include/sfx_pcm.h @@ -0,0 +1,235 @@ +/*************************************************************************** + sfx_pcm.h Copyright (C) 2003,04 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef _SFX_PCM_H_ +#define _SFX_PCM_H_ + +#include +#include +#include +#include + +/* A number of standard options most devices will support */ +#define SFX_PCM_OPTION_RATE "rate" /* Sampling rate: Number of samples per second */ +#define SFX_PCM_OPTION_BITS "bits" /* Sample size in bits */ +#define SFX_PCM_OPTION_STEREO "stereo" /* Whether to support stereo output */ +#define SFX_PCM_OPTION_BUF_SIZE "buffer-size" /* Requested buffer size */ +/* Device implementors are advised to use these constants whenever possible. */ + +#define SFX_PCM_MONO 0 +#define SFX_PCM_STEREO_LR 1 /* left sample, then right sample */ +#define SFX_PCM_STEREO_RL 2 /* right sample, then left sample */ + +/* The following are used internally by the mixer */ +#define SFX_PCM_FORMAT_LMASK 0x7 +#define SFX_PCM_FORMAT_BE 0 +#define SFX_PCM_FORMAT_LE 1 +#define SFX_PCM_FORMAT_ENDIANNESS 1 +#define SFX_PCM_FORMAT_8 0 +#define SFX_PCM_FORMAT_16 2 + + +/* Pick one of these formats (including the _NATIVE) ones for your PCM feed */ +#define SFX_PCM_FORMAT_U8 (0x0080 | SFX_PCM_FORMAT_8) /* Unsigned (bias 128) 8 bit format */ +#define SFX_PCM_FORMAT_S8 (0x0000 | SFX_PCM_FORMAT_8) /* Signed 8 bit format */ +#define SFX_PCM_FORMAT_U16_LE (0x8000 | SFX_PCM_FORMAT_16 | SFX_PCM_FORMAT_LE) /* Unsigned (bias 32768) 16 bit LE format */ +#define SFX_PCM_FORMAT_S16_LE (0x0000 | SFX_PCM_FORMAT_16 | SFX_PCM_FORMAT_LE) /* Signed 16 bit format, little endian */ +#define SFX_PCM_FORMAT_U16_BE (0x8000 | SFX_PCM_FORMAT_16 | SFX_PCM_FORMAT_BE) /* Unsigned (bias 32768) 16 bit BE format */ +#define SFX_PCM_FORMAT_S16_BE (0x0000 | SFX_PCM_FORMAT_16 | SFX_PCM_FORMAT_BE) /* Signed 16 bit format, big endian */ + +#ifdef WORDS_BIGENDIAN +# define SFX_PCM_FORMAT_U16_NATIVE SFX_PCM_FORMAT_U16_BE +# define SFX_PCM_FORMAT_S16_NATIVE SFX_PCM_FORMAT_S16_BE +#else +# define SFX_PCM_FORMAT_U16_NATIVE SFX_PCM_FORMAT_U16_LE +# define SFX_PCM_FORMAT_S16_NATIVE SFX_PCM_FORMAT_S16_LE +#endif + +#define SFX_PCM_FRAME_SIZE(conf) ((conf).stereo? 2 : 1) * (((conf).format & SFX_PCM_FORMAT_16)? 2 : 1) + + +typedef struct { + int nom, den; + int val; + + /* Total value: val + nom/den, where (nom < den) guaranteed. */ +} sfx_pcm_urat_t; /* Finitary unsigned rational numbers */ + +typedef struct { + int rate; /* Sampling rate */ + int stereo; /* The stereo mode used (SFX_PCM_MONO or SFX_PCM_STEREO_*) */ + unsigned int format; /* Sample format (SFX_PCM_FORMAT_*) */ +} sfx_pcm_config_t; + +typedef struct _sfx_pcm_device { + /* SFX devices are PCM players, i.e. output drivers for digitalised audio (sequences of audio samples). + ** Implementors are (in general) allowed to export specifics of these devices and let the mixer handle + ** endianness/signedness/bit size/mono-vs-stereo conversions. + */ + + const char *name; + const char *version; + + int (*init)(struct _sfx_pcm_device *self); + /* Initializes the device + ** Parameters: (sfx_pcm_device_t *) self: Self reference + ** Returns : (int) SFX_OK on success, SFX_ERROR if the device could not be + ** opened + ** This should attempt to open the highest quality output allowed by any options + ** specified beforehand. + */ + + void (*exit)(struct _sfx_pcm_device *self); + /* Uninitialises the device + ** Parameters: (sfx_pcm_device_t *) self: Self reference + */ + + int (*set_option)(struct _sfx_pcm_device *self, char *name, char *value); + /* Sets an option for the device + ** Parameters: (sfx_pcm_device_t *) self: Self reference + ** (char *) name: Name of the option to set + ** (char *) value: Value of the option to set + ** Returns : (int) SFX_OK on success, SFX_ERROR otherwise (unsupported option) + ** May be NULL + */ + + int (*output)(struct _sfx_pcm_device *self, byte *buf, + int count, sfx_timestamp_t *timestamp); + /* Writes output to the device + ** Parameters: (sfx_pcm_device_t *) self: Self reference + ** (byte *) buf: The buffer to write + ** (int) count: Number of /frames/ that should be written + ** (sfx_timestamp_t *) timestamp: Optional point in time + ** for which the PCM data is scheduled + ** Returns : (int) SFX_OK on success, SFX_ERROR on error + ** The size of the buffer allocated as 'buf' equals buf_size. + ** 'buf' is guaranteed not to be modified in between calls to 'output()'. + ** 'timestamp' is guaranteed to be used only in sequential order, but not + ** guaranteed to be used in all cases. It is guaranteed to be compaible with + ** the sample rate used by the device itself (i.e., the sfx_time.h functionality + ** is applicable) + */ + + sfx_timestamp_t + (*get_output_timestamp)(struct _sfx_pcm_device *self); + /* Determines the timestamp for 'output' + ** Parameters: (sfx_pcm_device_t *) self: Self reference + ** Returns : (sfx_timestamp_t) A timestamp (with the device's conf.rate) + ** describing the point in time at which + ** the next frame passed to 'output' + ** will be played + ** This function is OPTIONAL and may be NULL, but it is recommended + ** that pcm device implementers attempt to really implement it. + */ + + /* The following must be set after initialisation */ + sfx_pcm_config_t conf; + int buf_size; /* Output buffer size, i.e. the number of frames (!) + ** that can be queued by this driver before calling + ** output() will block or fail, drained according + ** to conf.rate */ + + /* The following are optional */ + sfx_timer_t *timer; + /* Many PCM drivers use a callback mechanism, which can be + ** exploited as a timer. Such a timer may be exported here and + ** will be preferred over other timers. */ + /* This is an _optional_ timer provided by the PCM + ** subsystem (may be NULL). It is checked for afer + ** initialisation, and used in preference to any + ** other timers available. + */ + void *internal; /* The private bits */ + +} sfx_pcm_device_t; + + +#define PCM_FEED_TIMESTAMP 0 /* New timestamp available */ +#define PCM_FEED_IDLE 1 /* No sound ATM, but new timestamp may be available later */ +#define PCM_FEED_EMPTY 2 /* Feed is finished, can be destroyed */ + +typedef struct _sfx_pcm_feed_t { + /* PCM feeds are sources of input for the PCM mixer. Their member functions + ** are invoked as callbacks on demand, to provide the mixer with input it + ** (in turn) passes on to PCM output devices. + ** PCM feeds must explicitly register themselves with the mixer in order + ** to be considered. + */ + + int (*poll)(struct _sfx_pcm_feed_t *self, byte *dest, int size); + /* Asks the PCM feed to write out the next stuff it would like to have written + ** Parameters: (sfx_pcm_feed_t *) self: Self reference + ** (byte *) dest: The destination buffer to write to + ** (int) size: The maximum number of _frames_ (not neccessarily bytes) + ** to write + ** Returns : (int) The number of frames written + ** If the number of frames written is smaller than 'size', the PCM feed will + ** be queried for a new timestamp afterwards, or destroyed if no new timestamp + ** is available. + */ + + void (*destroy)(struct _sfx_pcm_feed_t *self); + /* Asks the PCM feed to free all resources it occupies + ** Parameters: (sfx_pcm_feed_t *) self: Self reference + ** free(self) should be part of this function, if applicable. + */ + + int + (*get_timestamp)(struct _sfx_pcm_feed_t *self, sfx_timestamp_t *timestamp); + /* Determines the timestamp of the next frame-to-read + ** Returns : (sfx_timestamp_t) timestamp: The timestamp of the next frame + ** (int) PCM_FEED_* + ** This function is OPTIONAL and may be NULL + */ + + void *internal; /* The private bits of a PCM feed. */ + + sfx_pcm_config_t conf; /* The channel's setup */ + + const char *debug_name; /* The channel name, for debugging */ + int debug_nr; /* A channel number relative to the channel name, for debugging + ** (print in hex) */ + int frame_size; /* Frame size, computed by the mixer for the feed */ + +} sfx_pcm_feed_t; + +int +sfx_pcm_available(void); +/* Determines whether a PCM device is available and has been initialised +** Returns : (int) zero iff no PCM device is available +*/ + +sfx_pcm_device_t * +sfx_pcm_find_device(char *name); +/* Finds a PCM device by name +** Parameters: (char *) name: Name of the PCM device to look for, or NULL to +** use the system default +** Returns : (sfx_pcm_device_t *) The requested device, or NULL if no matching +** device could be found +*/ + +#endif /* !defined(_SFX_PCM_H_) */ diff --git a/engines/sci/include/sfx_player.h b/engines/sci/include/sfx_player.h new file mode 100644 index 0000000000..3198ff7c49 --- /dev/null +++ b/engines/sci/include/sfx_player.h @@ -0,0 +1,166 @@ +/*************************************************************************** + sfx_player.h Copyright (C) 2003,04 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* song player structure */ + +#include +#include +#include + +#ifndef _SFX_PLAYER_H +#define _SFX_PLAYER_H + +typedef void tell_synth_func(int buf_nr, byte *buf); + +typedef struct { + const char *name; + const char *version; + + int + (*set_option)(char *name, char *value); + /* Sets an option for player timing mechanism + ** Parameters: (char *) name: The name describing what to set + ** (char *) value: The value to set + ** Returns : (int) SFX_OK, or SFX_ERROR if the name wasn't understood + */ + + int + (*init)(resource_mgr_t *resmgr, int expected_latency); + /* Initializes the player + ** Parameters: (resource_mgr_t *) resmgr: A resource manager for driver initialization + ** (int) expected_latency: Expected delay in between calls to 'maintenance' + ** (in microseconds) + ** Returns : (int) SFX_OK on success, SFX_ERROR on failure + */ + + int + (*add_iterator)(song_iterator_t *it, GTimeVal start_time); + /* Adds an iterator to the song player + ** Parameters: (songx_iterator_t *) it: The iterator to play + ** (GTimeVal) start_time: The time to assume as the + ** time the first MIDI command executes at + ** Returns : (int) SFX_OK on success, SFX_ERROR on failure + ** The iterator should not be cloned (to avoid memory leaks) and + ** may be modified according to the needs of the player. + ** Implementors may use the 'sfx_iterator_combine()' function + ** to add iterators onto their already existing iterators + */ + + int + (*fade_out)(void); + /* Fades out the currently playing song (within two seconds + ** Returns : (int) SFX_OK on success, SFX_ERROR on failure + */ + + int + (*stop)(void); + /* Stops the currently playing song and deletes the associated iterator + ** Returns : (int) SFX_OK on success, SFX_ERROR on failure + */ + + int + (*iterator_message)(song_iterator_message_t msg); + /* Transmits a song iterator message to the active song + ** Parameters: (song_iterator_message_t) msg: The message to transmit + ** Returns : (int) SFX_OK on success, SFX_ERROR on failure + ** OPTIONAL -- may be NULL + ** If this method is not present, sending messages will stop + ** and re-start playing, so it is preferred that it is present + */ + + int + (*pause)(void); /* OPTIONAL -- may be NULL */ + /* Pauses song playing + ** Returns : (int) SFX_OK on success, SFX_ERROR on failure + */ + + int + (*resume)(void); /* OPTIONAL -- may be NULL */ + /* Resumes song playing after a pause + ** Returns : (int) SFX_OK on success, SFX_ERROR on failure + */ + + int + (*exit)(void); + /* Stops the player + ** Returns : (int) SFX_OK on success, SFX_ERROR on failure + */ + + void + (*maintenance)(void); /* OPTIONAL -- may be NULL */ + /* Regularly called maintenance function + ** This function is called frequently and regularly (if present), it can be + ** used to emit sound. + */ + + tell_synth_func *tell_synth; + /* Pass a raw MIDI event to the synth + Parameters: (int) argc: Length of buffer holding the midi event + (byte *) argv: The buffer itself + */ + + int polyphony; /* Number of voices that can play simultaneously */ + +} sfx_player_t; + +sfx_player_t * +sfx_find_player(char *name); +/* Looks up a player by name or finds the default player +** Parameters: (char *) name: Name of the player to look up, or NULL for dedault +** Returns : (sfx_player_t *) The player requested, or NULL if none was found +*/ + +tell_synth_func * +sfx_get_player_tell_func(void); +/* Gets the callback function of the player in use. +** Returns: (tell_synth_func *) The callback function. +*/ + +int +sfx_get_player_polyphony(void); +/* Determines the polyphony of the player in use +** Returns : (int) Number of voices the active player can emit +*/ + +void +sfx_reset_player(void); +/* Tells the player to stop its internal iterator +** Parameters: None. +** Returns: Nothing. + */ + +song_iterator_t * +sfx_iterator_combine(song_iterator_t *it1, song_iterator_t *it2); +/* Combines two song iterators into one +** Parameters: (sfx_iterator_t *) it1: One of the two iterators, or NULL +** (sfx_iterator_t *) it2: The other iterator, or NULL +** Returns : (sfx_iterator_t *) A combined iterator +** If a combined iterator is returned, it will be flagged to be allowed to +** dispose of 'it1' and 'it2', where applicable. This means that this +** call should be used by song players, but not by the core sound system +*/ + +#endif /* !_SFX_PLAYER_H */ diff --git a/engines/sci/include/sfx_songlib.h b/engines/sci/include/sfx_songlib.h new file mode 100644 index 0000000000..7aa6ea4fd5 --- /dev/null +++ b/engines/sci/include/sfx_songlib.h @@ -0,0 +1,200 @@ +/*************************************************************************** + sfx_songlib.h (C) 2002..04 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [reichenb@colorado.edu] + +***************************************************************************/ +/* Song library */ + +#ifndef _SCI_SFX_SONGLIB_H_ +#define _SCI_SFX_SONGLIB_H_ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include + +#define SOUND_STATUS_STOPPED 0 +#define SOUND_STATUS_PLAYING 1 +#define SOUND_STATUS_SUSPENDED 2 +/* suspended: only if ordered from kernel space */ +#define SOUND_STATUS_WAITING 3 +/* "waiting" means "tagged for playing, but not active right now" */ + +typedef unsigned long song_handle_t; + +typedef enum { + RESTORE_BEHAVIOR_CONTINUE, /* restart a song when restored from + a saved game */ + RESTORE_BEHAVIOR_RESTART /* continue it from where it was */ +} RESTORE_BEHAVIOR; + +typedef struct _song { + song_handle_t handle; + int resource_num; /* Resource number */ + int priority; /* Song priority (more important if priority is higher) */ + int status; /* See above */ + + int restore_behavior; + int restore_time; + +/* Grabbed from the sound iterator, for save/restore purposes */ + int loops; + int hold; + + song_iterator_t *it; + long delay; /* Delay before accessing the iterator, in microseconds */ + + GTimeVal wakeup_time; /* Used by the sound core: + ** Playing -> time at which 'delay' has elapsed + ** Suspended/Waiting -> stopping time */ + + struct _song *next; /* Next song or NULL if this is the last one */ + struct _song *next_playing; /* Next playing song; used by the + ** core song system */ + struct _song *next_stopping; /* Next song pending stopping; used exclusively by + ** the core song system's _update_multi_song() */ +} song_t; + + +typedef struct { + song_t **lib; + song_t *_s; +} songlib_t; + +/**************************/ +/* Song library commands: */ +/**************************/ + +song_t * +song_new(song_handle_t handle, song_iterator_t *it, int priority); +/* Initializes a new song +** Parameters: (song_handle_t) handle: The sound handle +** (song_iterator_t *) it: The song +** (int) priority: The song's priority +** Returns : (song_t *) A freshly allocated song +** Other values are set to predefined defaults. +*/ + + +void +song_lib_init(songlib_t *songlib); +/* Initializes a static song library +** Parameters: (songlib_t *) songlib: Pointer to the library +** to initialize +** Returns : (void) +*/ + +void +song_lib_free(songlib_t songlib); +/* Frees a song library +** Parameters: (songlib_t) songlib: The library to free +** Returns : (void) +*/ + +void +song_lib_add(songlib_t songlib, song_t *song); +/* Adds a song to a song library. +** Parameters: (songlib_t) songlib: An existing sound library, or NULL +** (song_t *) song: The song to add +** Returns : (void) +*/ + +song_t * +song_lib_find(songlib_t songlib, song_handle_t handle); +/* Looks up the song with the specified handle +** Parameters: (songlib_t) songlib: An existing sound library, may point to NULL +** (song_handle_t) handle: The sound handle to look for +** Returns : (song_t *) The song or NULL if it wasn't found +*/ + +song_t * +song_lib_find_active(songlib_t songlib); +/* Finds the first song playing with the highest priority +** Parameters: (songlib_t) songlib: An existing sound library +** Returns : (song_t *) The song that should be played next, or NULL if there is none +*/ + +song_t * +song_lib_find_next_active(songlib_t songlib, song_t *song); +/* Finds the next song playing with the highest priority +** Parameters: (songlib_t) songlib: The song library to operate on +** (song_t *) song: A song previously returned from the song library +** Returns : (song_t *) The next song to play relative to 'song', or +** NULL if none are left +** The functions 'song_lib_find_active' and 'song_lib_find_next_active +** allow to iterate over all songs that satisfy the requirement of +** being 'playable'. +*/ + +int +song_lib_remove(songlib_t songlib, song_handle_t handle); +/* Removes a song from the library +** Parameters: (songlib_t) songlib: An existing sound library +** (song_handle_t) handle: Handle of the song to remove +** Returns : (int) The status of the song that was removed +*/ + +void +song_lib_resort(songlib_t songlib, song_t *song); +/* Removes a song from the library and sorts it in again; for use after renicing +** Parameters: (songlib_t) songlib: An existing sound library +** (song_t *) song: The song to work on +** Returns : (void) +*/ + +int +song_lib_count(songlib_t songlib); +/* Counts the number of songs in a song library +** Parameters: (songlib_t) songlib: The library to count +** Returns : (int) The number of songs +*/ + +GTimeVal +song_sleep_time(GTimeVal *lastslept, long ticks); +/* Caluculates the amount of seconds and microseconds to sleep. +** Parameters: (GTimeVal *) lastslept: The time to start counting on +** (long) ticks: Number of ticks to sleep +** Returns : (GTimeVal) The amount of time to sleep +*/ + +GTimeVal +song_next_wakeup_time(GTimeVal *lastslept, long ticks); +/* Calculates the time at which "ticks" have passed, counting from "lastslept". +** Parameters: (GTimeVal *) lastslept: The base to start counting on +** (long) ticks: Number of ticks to count +** Returns : (GTimeVal) A structure describing the time at which the +** specified number of ticks has passed +*/ + +void +song_lib_set_restore_behavior(songlib_t songlib, song_handle_t handle, + RESTORE_BEHAVIOR action); +/* Determines what should be done with the song "handle" when +** restoring it from a saved game. +** Parameters: (songlib_t) songlib: The library that contains the song +** (song_handle_t) handle: Its handle +** (RESTORE_BEHAVIOR) action: The desired action +*/ + +#endif /* !_SCI_SOUND_SERVER_H_ */ diff --git a/engines/sci/include/sfx_time.h b/engines/sci/include/sfx_time.h new file mode 100644 index 0000000000..a7b65eae20 --- /dev/null +++ b/engines/sci/include/sfx_time.h @@ -0,0 +1,93 @@ +/*************************************************************************** + sfx_time.h Copyright (C) 2003,04 Christoph Reichenbach + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + + Please contact the maintainer for any program-related bug reports or + inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef _SFX_TIME_H_ +#define _SFX_TIME_H_ + +typedef struct { + long secs; + long usecs; + int frame_rate; + int frame_offset; + /* Total time: secs + usecs + frame_offset/frame_rate */ +} sfx_timestamp_t; + + +sfx_timestamp_t +sfx_new_timestamp(long secs, long usecs, int frame_rate); +/* Creates a new mutable timestamp +** Parameters: (long x long) (secs, usecs): Initial timestamp +** (int) frame_rate: Frame rate, for increasing the time stamp +*/ + +sfx_timestamp_t +sfx_timestamp_add(sfx_timestamp_t timestamp, int frames); +/* Adds a number of frames to a timestamp +** Parameters: (sfx_timestampt_t *) timestamp: The timestamp to update +** (int) frames: Number of frames to add +** Returns : (sfx_timestamp_t) The increased timestamp +*/ + +sfx_timestamp_t +sfx_timestamp_renormalise(sfx_timestamp_t timestamp, int new_freq); +/* Translates a timestamp to a new base frame frequency +** Parameters: (sfx_timestamp_t *) timestamp: The timestamp to normalise +** (int) new_freq: The new frequency to normalise to +** Returns : (sfx_timestamp_t) The re-normalised timestamp +** The translation looses accuracy in the order of magnitude of milliseconds +** for "usual" sampling frequencies. +*/ + +int +sfx_timestamp_frame_diff(sfx_timestamp_t a, sfx_timestamp_t b); +/* Computes the difference (# of frames) between two timestamps +** Parameters: (sfx_timestamp) a: See below +** (sfx_timestamp) b: See below +** Returns : (int) a-b +*/ + +long +sfx_timestamp_usecs_diff(sfx_timestamp_t a, sfx_timestamp_t b); +/* Computes the difference (# of microseconds) between two timestamps +** Parameters: (sfx_timestamp) a: See below +** (sfx_timestamp) b: See below +** Returns : (long) a-b +*/ + +void +sfx_timestamp_gettime(sfx_timestamp_t *timestamp, long *secs, long *usecs); +/* Determines the time described by a given timestamp +** Parameters: (sfx_timestamp_t *) timestamp: Timestamp to read from +** Returns : (int * x int *) (secs, usecs): Seconds and microseconds since +** the epoch described there +*/ + + + +#endif /* !defined(_SFX_TIME_H_) */ diff --git a/engines/sci/include/sfx_timer.h b/engines/sci/include/sfx_timer.h new file mode 100644 index 0000000000..d747edc3ac --- /dev/null +++ b/engines/sci/include/sfx_timer.h @@ -0,0 +1,96 @@ +/*************************************************************************** + sfx_timer.h Copyright (C) 2002..04 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef _FREESCI_SFX_TIMER_H_ +#define _FREESCI_SFX_TIMER_H_ + +#include + +typedef struct { + const char *name; + const char *version; + + int delay_ms; /* Approximate delay (in milliseconds) between calls */ + int flags; + + int + (*set_option)(char *name, char *value); + /* Sets an option for the timing mechanism + ** Parameters: (char *) name: The name describing what to set + ** (char *) value: The value to set + ** Returns : (int) SFX_OK, or SFX_ERROR if the name wasn't understood + ** May be NULL + */ + + int + (*init)(void (*callback)(void *data), void *data); + /* Initializes the timer + ** Parameters: (void* -> void) callback: + ** 'data' must contain the next argument: + ** (void *) data: Must always be passed to the callback + ** Returns : (int) SFX_OK on success, SFX_ERROR on failure + ** This does not start the timer yet, it just specifies and initializes it. + ** This function is called exactly once (provided that the timer is used at all). + */ + + int + (*exit)(void); + /* Stops the timer + ** Returns : (int) SFX_OK on success, SFX_ERROR on failure + ** All resources allocated with the timer should be freed as an effect + ** of this. + */ + + int + (*block)(void); + /* Blocks the timer + ** Returns : (int) SFX_OK on success, SFX_ERROR on failure + ** When this function returns it is guaranteed that no timer callback is + ** currently being executed and that no new timer callback will occur + ** until unblock() is called. + */ + + int + (*unblock)(void); + /* Unblocks the timer + ** Returns : (int) SFX_OK on success, SFX_ERROR on failure + ** Any callbacks that were blocked should be executed immediately when + ** possible. + */ +} sfx_timer_t; + +extern sfx_timer_t * +sfx_find_timer(char *name); +/* Finds a timer by name +** Parameters: (char *) name: Name of the timer to look up, or NULL for default +** Returns : (sfx_timer_t *) The timer of matching name, or NULL +** if not found +** This does not consider timers provided by PCM devices; there must be +** retrieved externally. +*/ + +#endif /* !_FREESCI_SFX_TIMER_H_ */ diff --git a/engines/sci/include/sys_strings.h b/engines/sci/include/sys_strings.h new file mode 100644 index 0000000000..7c47e6350b --- /dev/null +++ b/engines/sci/include/sys_strings.h @@ -0,0 +1,80 @@ +/*************************************************************************** + sys_strings.h Copyright (C) 2002 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef _FREESCI_SYSTEM_STRINGS_H_ +#define _FREESCI_SYSTEM_STRINGS_H_ + +#define SYS_STRINGS_MAX 4 + +#define SYS_STRING_SAVEDIR 0 +#define SYS_STRING_PARSER_BASE 1 + +#define MAX_PARSER_BASE 64 + +typedef struct { + char *name; + int max_size; + char *value; +} sys_string_t; + +typedef struct { + sys_string_t strings[SYS_STRINGS_MAX]; +} sys_strings_t; + +void +sys_string_acquire(sys_strings_t *strings, int index, const char *name, int max_len); +/* Reserves a new system string +** Parameters: (sys_strings_t *) strings: The string table to reserve in +** (int) index: Index number to reserve +** (const char *) name: Name the entry should be tagged with +** (int) max_len: Maximum string length in bytes +*/ + +int +sys_string_set(sys_strings_t *strings, int index, const char *value); +/* Sets the value of a system string +** Parameters: (sys_strings_t *) strings: The string table to use +** (int) index: Index of the string to write to +** (const char *) value: The value to copy +** Returns : 0 on success, 1 on error +** Length clipping is performed. +*/ + +void +sys_strings_restore(sys_strings_t *new_strings, sys_strings_t *old_strings); +/* Cleanup system strings after a gamestate restore +** Parameters: (sys_strings_t *) The freshly loaded system strings to clean up +** (sys_strings_t *) The old system strings to clean up +*/ + +void +sys_string_free_all(sys_strings_t *strings); +/* Deallocates all allocated system strings +** Parameters: (sys_strings_t *) strings: The string table to deallocate +*/ + +#endif /* !_FREESCI_SYSTEM_STRINGS_H_ */ diff --git a/engines/sci/include/uinput.h b/engines/sci/include/uinput.h new file mode 100644 index 0000000000..7e6ff4e749 --- /dev/null +++ b/engines/sci/include/uinput.h @@ -0,0 +1,130 @@ +/*************************************************************************** + uinput.h (C) 1999,2000,01,03 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + +***************************************************************************/ +/* unified input header file */ + +#ifndef _SCI_UINPUT_H +#define _SCI_UINPUT_H + +#ifdef HAVE_UNISTD_H +# include +#endif + + +struct _state; + +#define SCI_INPUT_DEFAULT_CLOCKTIME 100000 +#define SCI_INPUT_DEFAULT_REDRAWTIME 30000 + + +typedef struct { + short type; + short data; + short buckybits; + short character; /* for keyboard events: 'data' after applying + ** the effects of 'buckybits', e.g. if + ** type == SCI_EVT_KEYBOARD + ** data == 'a' + ** buckybits == SCI_EVM_LSHIFT + ** then + ** character == 'A' + ** For 'Alt', characters are interpreted by their + ** PC keyboard scancodes. + */ +} sci_event_t; + +/*Values for type*/ +#define SCI_EVT_NONE 0 +#define SCI_EVT_MOUSE_PRESS (1<<0) +#define SCI_EVT_MOUSE_RELEASE (1<<1) +#define SCI_EVT_KEYBOARD (1<<2) +#define SCI_EVT_JOYSTICK (1<<6) +#define SCI_EVT_SAID (1<<7) +/*Fake values for other events*/ +#define SCI_EVT_ERROR (1<<10) +#define SCI_EVT_QUIT (1<<11) +#define SCI_EVT_NONBLOCK (1<<15) +/* The QUIT event may be used to signal an external 'quit' command being +** issued to the gfx driver. */ +#define SCI_EVT_ANY 0x7fff + + + +/* Keycodes of special keys: */ +#define SCI_K_ESC 27 +#define SCI_K_BACKSPACE 8 +#define SCI_K_ENTER 13 +#define SCI_K_TAB '\t' +#define SCI_K_SHIFT_TAB (0xf << 8) + +#define SCI_K_END (79 << 8) +#define SCI_K_DOWN (80 << 8) +#define SCI_K_PGDOWN (81 << 8) +#define SCI_K_LEFT (75 << 8) +#define SCI_K_CENTER (76 << 8) +#define SCI_K_RIGHT (77 << 8) +#define SCI_K_HOME (71 << 8) +#define SCI_K_UP (72 << 8) +#define SCI_K_PGUP (73 << 8) +#define SCI_K_INSERT (82 << 8) +#define SCI_K_DELETE (83 << 8) + +#define SCI_K_F1 (59<<8) +#define SCI_K_F2 (60<<8) +#define SCI_K_F3 (61<<8) +#define SCI_K_F4 (62<<8) +#define SCI_K_F5 (63<<8) +#define SCI_K_F6 (64<<8) +#define SCI_K_F7 (65<<8) +#define SCI_K_F8 (66<<8) +#define SCI_K_F9 (67<<8) +#define SCI_K_F10 (68<<8) + +#define SCI_K_SHIFT_F1 (84<<8) +#define SCI_K_SHIFT_F2 (85<<8) +#define SCI_K_SHIFT_F3 (86<<8) +#define SCI_K_SHIFT_F4 (87<<8) +#define SCI_K_SHIFT_F5 (88<<8) +#define SCI_K_SHIFT_F6 (89<<8) +#define SCI_K_SHIFT_F7 (90<<8) +#define SCI_K_SHIFT_F8 (91<<8) +#define SCI_K_SHIFT_F9 (92<<8) +#define SCI_K_SHIFT_F10 (93<<8) + +/*Values for buckybits */ +#define SCI_EVM_RSHIFT (1<<0) +#define SCI_EVM_LSHIFT (1<<1) +#define SCI_EVM_CTRL (1<<2) +#define SCI_EVM_ALT (1<<3) +#define SCI_EVM_SCRLOCK (1<<4) +#define SCI_EVM_NUMLOCK (1<<5) +#define SCI_EVM_CAPSLOCK (1<<6) +#define SCI_EVM_INSERT (1<<7) + +#define SCI_EVM_NO_FOOLOCK (~(SCI_EVM_SCRLOCK | SCI_EVM_NUMLOCK | SCI_EVM_CAPSLOCK | SCI_EVM_INSERT)) +#define SCI_EVM_ALL 0xFF + +#endif /* _SCI_UINPUT_H */ diff --git a/engines/sci/include/util.h b/engines/sci/include/util.h new file mode 100644 index 0000000000..75ec2e6706 --- /dev/null +++ b/engines/sci/include/util.h @@ -0,0 +1,108 @@ +/*************************************************************************** + util.h Copyright (C) 1999,2000,01 Magnus Reftel + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef UTIL_H +#define UTIL_H + +#include +#include + +/* An excercise in bloated macro writing ;-)*/ + +/*Declare a flexarray type*/ +#define FLEXARRAY(type, extra) struct{\ + type* data;\ + int used, size, itemsize;\ + extra\ +} + +/* [DJ] Declare a flexarray type without extra fields - Visual C++ complains +** when the FLEXARRAY macro is used and extra parameter is not specified +*/ +#define FLEXARRAY_NOEXTRA(type) struct{\ + type* data;\ + int used, size, itemsize;\ +} + +/*Initialize a flexarray*/ +#define FLEXARRAY_INIT(type, array) do {(array).itemsize=sizeof(type); (array).size=0; (array).used=0;} while(0) + +/*Prints an error message and returns (from the function using the macro)*/ +#define FLEXARRAY_PANIC(message, errorcode) do{\ + fprintf message;\ + errorcode;\ +} while(0) + +/*Use if the state of the array is incnsistent*/ +#define FLEXARRAY_PANIC_BADSTATE(array, errorcode) FLEXARRAY_PANIC((stderr, "PANIC: flexarray "#array" is in an inconsistent state (%d,%d).\n", (array).used, (array).size), errorcode) + +/*Use if memory allocation fails*/ +#define FLEXARRAY_PANIC_MEMORY(array, bytes, errorcode) FLEXARRAY_PANIC((stderr, "PANIC: Unable to allocate %d bytes for flexarray"#array"\n", bytes), errorcode) + +/*Allocates an initial array*/ +#define FLEXARRAY_ALLOCATE_INITIAL(type, array, errorcode) do{\ + (array).data= (type*)sci_malloc(4*(array).itemsize);\ + if((array).data==0) FLEXARRAY_PANIC_MEMORY(array, 4*(array).itemsize, errorcode);\ + (array).size=4;\ + (array).used=0;\ +} while(0) + +/*Doubles the size of the allocated area*/ +#define FLEXARRAY_RESIZE(type, array, errorcode) do{\ + int size=(array).size*2*(array).itemsize;\ + (array).data= (type*)sci_realloc((array).data, size);\ + if((array).data==0) FLEXARRAY_PANIC_MEMORY(array, size, errorcode);\ + (array).size*=2;\ +} while(0) + +/*Appends /value/ at the end of the array, resizing as necessary*/ +#define FLEXARRAY_APPEND(type, array, value, errorcode) do\ +{\ + if((array).used>=(array).size)\ + {\ + if((array).size==0) FLEXARRAY_ALLOCATE_INITIAL(type, array, errorcode);\ + else {\ + if((array).used==(array).size) FLEXARRAY_RESIZE(type, array, errorcode);\ + else FLEXARRAY_PANIC_BADSTATE(array, errorcode);\ + }\ + }\ + (array).data[(array).used++]=(value);\ +}while(0) + +/*Adds space for a value at the end of the array, resizing as necessary, but + *does not initialize the value*/ +#define FLEXARRAY_ADD_SPACE(type, array, items, errorcode) do{\ + if((array).used+items>(array).size)\ + {\ + if((array).size==0) FLEXARRAY_ALLOCATE_INITIAL(type, array, errorcode);\ + else if((array).used==(array).size) FLEXARRAY_RESIZE(type, array, errorcode);\ + else FLEXARRAY_PANIC_BADSTATE(array, errorcode);\ + }\ + (array).used++;\ +} while(0) + +#endif diff --git a/engines/sci/include/versions.h b/engines/sci/include/versions.h new file mode 100644 index 0000000000..ed184d29c0 --- /dev/null +++ b/engines/sci/include/versions.h @@ -0,0 +1,169 @@ + +/*************************************************************************** + versions.h Copyright (C) 1999,2000,01 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + + +***************************************************************************/ +/* Versions management */ + +#ifndef _SCI_VERSIONS_H_ +#define _SCI_VERSIONS_H_ + +#include + +struct _state; + +#define SCI_VERSION(_major_, _minor_, _patchlevel_) (((_major_)<<20) | ((_minor_)<<10) | _patchlevel_) +/* This allows version numbers to be compared directly */ + +#define SCI_VERSION_MAJOR(_version_) ((_version_) >> 20) +#define SCI_VERSION_MINOR(_version_) (((_version_) >> 10) & 0x3ff) +#define SCI_VERSION_PATCHLEVEL(_version_) ((_version_) & 0x3ff) +#define SCI_VERSION_IGNORE_PATCHLEVEL(_version_) ((_version) & ~0x3ff) + +/* Version number guide: +** - Always use the version number of the first known version to have a special feature. +** - Don't assume that special feature changes are linked just because they appeared to change +** simultaneously. +** - Put all the magic version numbers here, into THIS file. +** - "FTU" means "First To Use" +*/ + +#define SCI_VERSION_LAST_SCI0 SCI_VERSION(0,000,685) + +#define SCI_VERSION_DEFAULT_SCI0 SCI_VERSION_LAST_SCI0 +/* AFAIK this is the last published SCI0 version */ +#define SCI_VERSION_DEFAULT_SCI01 SCI_VERSION(1,000,72) +/* The version used by my implementation of QfG2 */ + + +#define SCI_VERSION_FTU_CENTERED_TEXT_AS_DEFAULT SCI_VERSION(0,000,629) +/* Last version known not to do this: 0.000.502 */ + +#define SCI_VERSION_FTU_NEW_GETTIME SCI_VERSION(0,000,629) +/* These versions of SCI has a different set of subfunctions in GetTime() */ + +#define SCI_VERSION_FTU_NEWER_DRAWPIC_PARAMETERS SCI_VERSION(0,000,502) +/* Last version known not to do this: 0.000.435 +** Old SCI versions used to interpret the third DrawPic() parameter inversely, +** with the opposite default value (obviously) +*/ + +#define SCI_VERSION_FTU_PRIORITY_14_ZONES SCI_VERSION(0,000,502) +/* Last version known to do this: 0.000.490 + * Uses 14 zones from 42 to 190 instead of 15 zones from 42 to 200. +*/ + + +#define SCI_VERSION_FTU_NEW_SCRIPT_HEADER SCI_VERSION(0,000,395) +/* Last version known not to do this: 0.000.343 +** Old SCI versions used two word header for script blocks (first word equal +** to 0x82, meaning of the second one unknown). New SCI versions used one +** word header. +*/ + +#define SCI_VERSION_LTU_BASE_OB1 SCI_VERSION(0,000,256) +/* First version version known not to have this bug: ? +** When doing CanBeHere(), augment y offset by 1 +*/ + +#define SCI_VERSION_FTU_2ND_ANGLES SCI_VERSION(0,000,395) +/* Last version known not to use this: ? +** Earlier versions assign 120 degrees to left & right , and 60 to up and down. +** Later versions use an even 90 degree distribution. +*/ + +#define SCI_VERSION_RESUME_SUSPENDED_SONG SCI_VERSION(0,000,490) +/* First version (PQ2-new) known to use the different song resumption + mechanism -- When a new song is initialized, we store its state and + resume it when the new one finishes. Older versions completely + clobbered the old songs. +*/ + +#define SCI_VERSION_FTU_INVERSE_CANBEHERE SCI_VERSION(1,000,510) +/* FIXME: This shouldn't be a version number. + * But it'll do for now. + */ + +#define SCI_VERSION_FTU_LOFS_ABSOLUTE SCI_VERSION(1,000,200) +/* First version known to do this: ? + In later versions (SCI1 and beyond), the argument of lofs[as] + instructions is absolute rather than relative. +*/ + +#define SCI_VERSION_FTU_DISPLAY_COORDS_FUZZY SCI_VERSION(1,000,510) +/* First version known to do this: ? + In later versions of SCI1 kDisplay(), if the text would not fit on + the screen, the text is moved to the left and upwards until it + fits. +*/ + +#define SCI_VERSION_FTU_DOSOUND_VARIANT_1 SCI_VERSION(1,000,000) +#define SCI_VERSION_FTU_DOSOUND_VARIANT_2 SCI_VERSION(1,000,510) + + +typedef int sci_version_t; + +struct _state; + +void +version_require_earlier_than(struct _state *s, sci_version_t version); +/* Function used in autodetection +** Parameters: (state_t *) s: state_t containing the version +** (sci_version_t) version: The version that we're earlier than +*/ + +void +version_require_later_than(struct _state *s, sci_version_t version); +/* Function used in autodetection (read this function "version_require_later_than_or_equal_to") +** Parameters: (state_t *) s: state_t containing the version +** (sci_version_t) version: The version that we're later than +*/ + +int +version_parse(char *vn, sci_version_t *result); +/* Parse a string containing an SCI version number +** Parameters: (char *) vn: The string to parse +** Returns : (int) 0 on success, 1 on failure +** (sci_version_t) *result: The resulting version number on success +*/ + +int +version_detect_from_executable(sci_version_t *result); +/* Try to detect version from Sierra executable in cwd +** Returns : (int) 0 on success, 1 on failure +** (sci_version_t) *result: The version number detected on success +*/ + +const char * +version_guess_from_hashcode(sci_version_t *result, int *res_version, guint32 *code); +/* Try to detect version from Sierra resource file(s) in cwd +** Returns : (const char *) NULL on failure, the name of the associated game otherwise +** (sci_version_t) *result: The version number detected on success +** (int) *res_version: The resource version number detected on success +** (guint32) *code: The resource hash code +*/ + +#endif /* !_SCI_VERSIONS_H_ */ diff --git a/engines/sci/include/vm.h b/engines/sci/include/vm.h new file mode 100644 index 0000000000..567df073f5 --- /dev/null +++ b/engines/sci/include/vm.h @@ -0,0 +1,843 @@ +/*************************************************************************** + vm.h Copyright (C) 2000,2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +/* VM and kernel declarations */ + + +#include +#include +#include +#include +#include +#include +#include + +#ifndef _SCI_VM_H +#define _SCI_VM_H + +#ifdef __cplusplus +# define new new_ +# define delete delete_ +# define class class_ +#endif /* __cplusplus */ + +#define VM_STACK_SIZE 0x1000 +/* Number of bytes to be allocated for the stack */ + +#define SCRIPT_MAX_EXEC_STACK 256 +/* Maximum number of calls residing on the stack */ +#define SCRIPT_MAX_CLASSTABLE_SIZE 256 +/* Maximum number of entries in the class table */ +#define SCRIPT_MAX_CLONES 256 +/* Maximum number of cloned objects on the heap */ + + +#define SCRIPT_SELECTOR_OFFSET 8 -8 +/* Object-relative offset of the selector area inside a script */ + +#define SCRIPT_LOCALVARPTR_OFFSET 2 -8 +/* Object-relative offset of the pointer to the underlying script's local variables */ + +#define SCRIPT_SELECTORCTR_OFFSET 6 -8 +/* Object-relative offset of the selector counter */ + +#define SCRIPT_FUNCTAREAPTR_OFFSET 4 -8 +/* Object-relative offset of the offset of the function area */ + +#define SCRIPT_FUNCTAREAPTR_MAGIC 8 -8 +/* Offset that has to be added to the function area pointer */ + +#define SCRIPT_NAME_OFFSET (s->version < SCI_VERSION(1,001,000) ? 14 -8 : 16) +/* Offset of the name pointer */ +#define SCRIPT_NAME_SELECTOR (s->version < SCI_VERSION(1,001,000) ? 3 : 8) + +#define SCRIPT_INFO_OFFSET (s->version < SCI_VERSION(1,001,000) ? 12 -8 : 14) +/* Object-relative offset of the -info- selector */ +#define SCRIPT_INFO_SELECTOR (s->version < SCI_VERSION(1,001,000) ? 2 : 7) + +#define SCRIPT_INFO_CLONE 0x0001 +/* Flag fo the -info- selector */ + +#define SCRIPT_INFO_CLASS 0x8000 +/* Flag for the -info- selector */ + + +#define SCRIPT_OBJECT_MAGIC_NUMBER 0x1234 +/* Magical object identifier */ +#define SCRIPT_OBJECT_MAGIC_OFFSET (s->version < SCI_VERSION(1,001,000) ? -8 : 0) +/* Offset of this identifier */ + +#define SCRIPT_SPECIES_OFFSET 8 -8 +/* Script-relative offset of the species ID */ + +#define SCRIPT_SUPERCLASS_OFFSET (s->version < SCI_VERSION(1,001,000) ? 10 -8 : 12) + +/*---------------------------------*/ +/* Script selector index variables */ +/*---------------------------------*/ +#define SCRIPT_SPECIES_SELECTOR (s->version < SCI_VERSION(1,001,000) ? 0 : 5) +#define SCRIPT_SUPERCLASS_SELECTOR (s->version < SCI_VERSION(1,001,000) ? 1 : 6) + +#define SCRIPT_LOFS_MAGIC 3 +/* Magic adjustment value for lofsa and lofss */ + + +#define CALL_SP_CARRY NULL /* Stack pointer value: Use predecessor's value */ + + +#define SELECTOR_NONE 0 +#define SELECTOR_VARIABLE 1 +#define SELECTOR_METHOD 2 +/* Types of selectors as returned by grep_selector() below */ + +typedef struct { + int script; /* number of the script the class is in, -1 for non-existing */ + reg_t reg; /* offset; script-relative offset, segment: 0 if not instantiated */ +} class_t; + +#define RAW_GET_CLASS_INDEX(scr, reg) (int_hash_map_check_value((scr)->obj_indices, reg.offset, 0, NULL)) +#define RAW_IS_OBJECT(datablock) (getUInt16(((byte *) datablock) + SCRIPT_OBJECT_MAGIC_OFFSET) == SCRIPT_OBJECT_MAGIC_NUMBER) + +#define IS_CLASS(obj) (obj->variables[SCRIPT_INFO_SELECTOR].offset & SCRIPT_INFO_CLASS) + +/* This struct is used to buffer the list of send calls in send_selector() */ +typedef struct { + union { + reg_t func; + reg_t *var; + } address; + stack_ptr_t argp; + int argc; + selector_t selector; + stack_ptr_t sp; /* Stack pointer */ + int type; /* Same as exec_stack_t.type */ +} calls_struct_t; + +typedef struct { + int script_id; /* Script ID this local variable block belongs to */ + reg_t *locals; + int nr; +} local_variables_t; + +#define OBJECT_FLAG_FREED (0x1 << 0) /* Clone has been marked as 'freed' */ + +typedef struct { + int flags; + reg_t pos; /* Object offset within its script; for clones, this is their base */ + int variables_nr; + int variable_names_nr; /* Number of variable names, may be less than variables_nr */ + int methods_nr; + byte *base; /* Points to a buffer all relative references (code, strings) point to */ + byte *base_obj; /* base + object offset within base */ + guint16 *base_method; /* Pointer to the method selector area for this object */ + guint16 *base_vars; /* Pointer to the varselector area for this object */ + reg_t *variables; +} object_t; + +typedef struct { + reg_t pos; + int size; +} code_block_t; + +#define VM_OBJECT_GET_VARSELECTOR(obj, i) \ + (s->version < SCI_VERSION(1,001,000) ? \ + getUInt16(obj->base_obj + obj->variables_nr * 2 + i*2) : \ + *(obj->base_vars + i)) +#define VM_OBJECT_READ_PROPERTY(obj, i) (obj->variables[i]) +#define VM_OBJECT_GET_FUNCSELECTOR(obj, i) \ + (s->version < SCI_VERSION(1,001,000) ? \ + getUInt16((byte *) (obj->base_method + i)) : \ + getUInt16((byte *) (obj->base_method + i*2 + 1))) +#define VM_OBJECT_READ_FUNCTION(obj, i) \ + (s->version < SCI_VERSION(1,001,000) ? \ + make_reg(obj->pos.segment, \ + getUInt16((byte *) (obj->base_method \ + + obj->methods_nr + 1 \ + + i))) : \ + make_reg(obj->pos.segment, \ + getUInt16((byte *) (obj->base_method \ + + i * 2 + 2)))) + + + + +//#define VM_OBJECT_SET_INDEX(ptr, index) { ((byte *) (ptr))[0] = (index) & 0xff; ((byte *) (ptr))[1] = ((index) >> 8) & 0xff; } +#define VM_OBJECT_GET_INDEX(scr, reg) (int_hash_map_check_value(scr->obj_indices, reg.offset, 0, NULL)) + +typedef struct { + int nr; /* Script number */ + byte* buf; /* Static data buffer, or NULL if not used */ + size_t buf_size; + size_t script_size; + size_t heap_size; + + byte *synonyms; /* Synonyms block or 0 if not present*/ + byte *heap_start; /* Start of heap if SCI1.1, NULL otherwise */ + guint16 *export_table; /* Abs. offset of the export table or 0 if not present */ + + int_hash_map_t *obj_indices; + + int exports_nr; /* Number of entries in the exports table */ + int synonyms_nr; /* Number of entries in the synonyms block */ + int lockers; /* Number of classes and objects that require this script */ + + object_t *objects; /* Table for objects, contains property variables */ + /* Indexed by the value stored at SCRIPT_LOCALVARPTR_OFFSET, + ** see VM_OBJECT_[GS]ET_INDEX() */ + int objects_nr; /* Number of objects and classes */ + int objects_allocated; /* Number of allocated objects */ + + int locals_offset; + int locals_segment; /* The local variable segment */ + local_variables_t *locals_block; + + code_block_t *code; + int code_blocks_nr; + int code_blocks_allocated; + int relocated; + int marked_as_deleted; +} script_t; + +typedef struct { + int nr; /* Number of stack entries */ + reg_t *entries; +} dstack_t; /* Data stack */ + +#define CLONE_USED -1 +#define CLONE_NONE -1 + +typedef object_t clone_t; + +typedef struct _node_struct { + reg_t pred, succ; /* Predecessor, successor */ + reg_t key; + reg_t value; +} node_t; /* List nodes */ + +typedef struct _list_struct { + reg_t first; + reg_t last; +} list_t; + +typedef struct { + void *mem; + unsigned int size; + const char *type; +} hunk_t; + +/* clone_table_t */ +DECLARE_HEAPENTRY(clone) +/* node_table_t */ +DECLARE_HEAPENTRY(node) +/* list_table_t */ +DECLARE_HEAPENTRY(list) /* list entries */ +/* hunk_table_t */ +DECLARE_HEAPENTRY(hunk) + +typedef struct { + int size; + const char *description; + byte *buf; +} dynmem_t; /* Free-style memory */ + +typedef struct _mem_obj { + int type; + int segmgr_id; /* Internal value used by the seg_manager's hash map */ + union { + script_t script; + clone_table_t clones; + local_variables_t locals; + dstack_t stack; + sys_strings_t sys_strings; + list_table_t lists; + node_table_t nodes; + hunk_table_t hunks; + dynmem_t dynmem; + char *reserved; + } data; +} mem_obj_t; + + + +typedef struct { + selector_t init; /* Init function */ + selector_t play; /* Play function (first function to be called) */ + selector_t replay; /* Replay function */ + selector_t x, y, z; /* Coordinates */ + selector_t priority; + selector_t view, loop, cel; /* Description of a specific image */ + selector_t brLeft, brRight, brTop, brBottom; /* Bounding Rectangle */ + selector_t xStep, yStep; /* BR adjustments */ + selector_t nsLeft, nsRight, nsTop, nsBottom; /* View boundaries ('now seen') */ + selector_t text, font; /* Used by controls */ + selector_t type, state; /* Used by contols as well */ + selector_t doit; /* Called (!) by the Animate() system call */ + selector_t signal; /* Used by Animate() to control a view's behaviour */ + selector_t underBits; /* Used by the graphics subroutines to store backupped BG pic data */ + + /* The following selectors are used by the Bresenham syscalls: */ + selector_t canBeHere; /* Funcselector: Checks for movement validity */ + selector_t client; /* The object that wants to be moved */ + selector_t cycler; /* The cycler of the client */ + selector_t dx, dy; /* Deltas */ + selector_t edgeHit; + selector_t b_movCnt, b_i1, b_i2, b_di, b_xAxis, b_incr; /* Various Bresenham vars */ + selector_t completed; + + selector_t illegalBits; /* Used by CanBeHere */ + selector_t dispose; + + selector_t prevSignal; /* Used by DoSound */ + + selector_t message, modifiers; /* Used by GetEvent */ + + selector_t owner, handle; + selector_t cue; + selector_t number; + + selector_t max, cursor; /* Used by EditControl */ + selector_t mode; /* Used by text controls (-> DrawControl()) */ + + selector_t wordFail, syntaxFail, semanticFail; /* Used by Parse() */ + + selector_t claimed; /* Used generally by the event mechanism */ + + selector_t elements; /* Used by SetSynonyms() */ + + selector_t lsTop, lsBottom, lsRight, lsLeft; /* Used by Animate() subfunctions and scroll list controls */ + + selector_t baseSetter; /* Alternative baseSetter */ + + selector_t who, distance; /* Used for 'chasing' movers */ + + selector_t looper, mover, isBlocked, heading; /* Used in DoAvoider */ + + selector_t caller, moveDone, moveSpeed; /* Used for DoBresen */ + + selector_t delete; /* Called by Animate() to dispose a view object */ + + selector_t vol; + selector_t pri; + + selector_t min; /* SMPTE time format */ + selector_t sec; + selector_t frame; + + selector_t dataInc; + selector_t size; + selector_t palette; + selector_t cantBeHere; + selector_t nodePtr; + selector_t flags; + + selector_t points; /* Used by AvoidPath() */ +} selector_map_t; /* Contains selector IDs for a few selected selectors */ + +typedef struct { + reg_t obj; + reg_t *signalp; /* Used only indirectly */ + reg_t *underBitsp; /* The same goes for the handle storage */ + int underBits; /* Copy of the underbits: Needed for cleanup */ + + int x, y; + int priority; + byte *view; + int view_nr, loop, cel; /* view_nr is ised for save/restore */ + int nsTop, nsLeft, nsRight, nsBottom; + int real_y, z, index_nr; /* Used for sorting */ +} view_object_t; + +#define VAR_GLOBAL 0 +#define VAR_LOCAL 1 +#define VAR_TEMP 2 +#define VAR_PARAM 3 + +#define EXEC_STACK_TYPE_CALL 0 +#define EXEC_STACK_TYPE_KERNEL 1 +#define EXEC_STACK_TYPE_VARSELECTOR 2 + +typedef struct { + reg_t objp; + reg_t sendp; /* Pointer to the object containing the invoked method */ + union { + reg_t *varp; /* Variable pointer for read/write access */ + reg_t pc; /* Not accurate for the TOS element */ + } addr; + stack_ptr_t fp; /* Frame pointer */ + stack_ptr_t sp; /* Stack pointer */ + int argc; + + /* former variables[4]: [all other values are derived] */ + stack_ptr_t variables_argp; /* Argument pointer */ + seg_id_t local_segment; /* local variables etc. */ + + selector_t selector; /* The selector which was used to call or -1 if not applicable */ + int origin; /* The stack frame position the call was made from, or -1 if it + ** was the initial call. */ + byte type; /* EXEC_STACK_TYPE* */ + +} exec_stack_t; + +typedef struct _breakpoint { + int type; + union { + guint32 address; /* Breakpoints on exports */ + char *name; /* Breakpoints on selector names */ + } data; + struct _breakpoint *next; +} breakpoint_t; + +#define BREAK_SELECTOR 1 +/* Break when selector is executed. data contains (char *) selector name + (in the format Object::Method) */ + +#define BREAK_EXPORT 2 +/* Break when an exported function is called. data contains script_no << 16 | + export_no. */ + +extern DLLEXTERN int script_debug_flag; +/* Set this to 1 to activate script debugging */ + +extern int script_error_flag; +/* Set to 1 to move pc back to last position, even though action is executed */ + +extern int script_checkloads_flag; +/* Displays the numbers of scripts when they are (un)loaded */ + +#define SCRIPT_ABORT_WITH_REPLAY 1025 +extern DLLEXTERN int script_abort_flag; +/* Set this to 1 to abort script execution immediately. Aborting will leave the +** debug exec stack intact. +** Set it to SCRIPT_ABORT_WITH_REPLAY to force a replay afterwards. +*/ + +#define GC_INTERVAL 32768 /* Number of kernel calls in between gcs; should be < 50000 */ + +extern int script_gc_interval; +/* Initially GC_DELAY, can be set at runtime */ + +extern int script_step_counter; +/* Number of steps executed */ + + +extern DLLEXTERN const char *(*_debug_get_input)(void); +/* The function used to get input for debugging */ + +extern DLLEXTERN int _debugstate_valid; +extern DLLEXTERN int _debug_seeking; +extern DLLEXTERN int _debug_step_running; + + +typedef int kernel_function(struct _state* s); + +extern kernel_function* kfuncs[]; +extern int max_instance; + +/*inline*/ exec_stack_t * +execute_method(struct _state *s, word script, word pubfunct, stack_ptr_t sp, reg_t calling_obj, + word argc, stack_ptr_t argp); +/* Executes function pubfunct of the specified script. +** Parameters: (state_t *) s: The state which is to be executed with +** (word) script: The script which is called +** (word) pubfunct: The exported script function which is to be called +** (stack_ptr_t) sp: Stack pointer position +** (reg_t) calling_obj: The heap address of the object which executed the call +** (word) argc: Number of arguments supplied +** (stack_ptr_t) argp: Pointer to the first supplied argument +** Returns : (exec_stack_t *): A pointer to the new exec stack TOS entry +*/ + + +exec_stack_t * +send_selector(struct _state *s, reg_t send_obj, reg_t work_obj, + stack_ptr_t sp, int framesize, stack_ptr_t argp); +/* Executes a "send" or related operation to a selector +** Parameters: (state_t *) s: The state_t to operate on +** (reg_t) send_obj: Heap address of the object to send to +** (reg_t) work_obj: Heap address of the object initiating the send +** (stack_ptr_t) sp: Stack pointer position +** (int) framesize: Size of the send as determined by the "send" operation +** (stack_ptr_t) argp: Pointer to the beginning of the heap block containing the +** data to be send. This area is a succession of one or more +** sequences of [selector_number][argument_counter] and then +** "argument_counter" word entries with the parameter values. +** Returns : (exec_stack_t *): A pointer to the new execution stack TOS entry +*/ + + +#define SCI_XS_CALLEE_LOCALS -1 + +exec_stack_t * +add_exec_stack_entry(struct _state *s, reg_t pc, stack_ptr_t sp, reg_t objp, int argc, + stack_ptr_t argp, selector_t selector, reg_t sendp, int origin, + seg_id_t local_segment); +/* Adds an entry to the top of the execution stack +** Parameters: (state_t *) s: The state with which to execute +** (reg_t) pc: The initial program counter +** (stack_ptr_t) sp: The initial stack pointer +** (reg_t) objp: Pointer to the beginning of the current object +** (int) argc: Number of parameters to call with +** (stack_ptr_t) argp: Heap pointer to the first parameter +** (selector_t) selector: The selector by which it was called or +** NULL_SELECTOR if n.a. For debugging. +** (reg_t) sendp: Pointer to the object which the message was sent to. +** Equal to objp for anything but super. +** (int) origin: Number of the execution stack element this entry was created by +** (usually the current TOS number, except for multiple sends). +** (seg_id_t) local_segment: The segment to use for local variables, +** or SCI_XS_CALLEE_LOCALS to use obj's segment. +** Returns : (exec_stack_t *): A pointer to the new exec stack TOS entry +*/ + + +exec_stack_t * +add_exec_stack_varselector(struct _state *s, reg_t objp, int argc, stack_ptr_t argp, + selector_t selector, reg_t *address, int origin); +/* Adds one varselector access to the execution stack +** Parameters: (state_t *) s: The state_t to use +** (reg_t) objp: Pointer to the object owning the selector +** (int) argc: 1 for writing, 0 for reading +** (stack_ptr_t) argp: Pointer to the address of the data to write -2 +** (int) selector: Selector name +** (reg_t *) address: Heap address of the selector +** (int) origin: Stack frame which the access originated from +** Returns : (exec_stack_t *): Pointer to the new exec-TOS element +** This function is called from send_selector only. +*/ + + +void +run_vm(struct _state *s, int restoring); +/* Executes the code on s->heap[pc] until it hits a 'ret' operation while (stack_base == stack_pos) +** Parameters: (state_t *) s: The state to use +** (int) restoring: 1 if s has just been restored, 0 otherwise +** Returns : (void) +** This function will execute SCI bytecode. It requires s to be set up +** correctly. +*/ + +void +vm_handle_fatal_error(struct _state *s, int line, const char *file); +/* Handles a fatal error condition +** Parameters: (state_t *) s: The state to recover from +** (int) line: Source code line number the error occured in +** (const char *) file: File the error occured in +*/ + + +void +script_debug(struct _state *s, reg_t *pc, stack_ptr_t *sp, stack_ptr_t *pp, reg_t *objp, + int *restadjust, + seg_id_t *segids, reg_t **variables, reg_t **variables_base, + int *variables_nr, + int bp); +/* Debugger functionality +** Parameters: (state_t *) s: The state at which debugging should take place +** (reg_t *) pc: Pointer to the program counter +** (stack_ptr_t *) sp: Pointer to the stack pointer +** (stack_ptr_t *) pp: Pointer to the frame pointer +** (reg_t *) objp: Pointer to the object base pointer +** (int *) restadjust: Pointer to the &rest adjustment value +** (seg_id_t *) segids: four-element array containing segment IDs for locals etc. +** (reg_t **) variables: four-element array referencing registers for globals etc. +** (reg_t **) variables_base: four-element array referencing +** register bases for temps etc. +** (int *) variables_nr: four-element array giving sizes for params etc. (may be NULL) +** (int) bp: Flag, set to 1 when a breakpoint is triggered +** Returns : (void) +*/ + +int +script_init_engine(struct _state *s, sci_version_t version); +/* Initializes a state_t block +** Parameters: (state_t *) s: The state to initialize +** Returns : 0 on success, 1 if vocab.996 (the class table) is missing or corrupted +*/ + +void +script_set_gamestate_save_dir(struct _state *s, const char *path); +/* Sets the gamestate's save_dir to the parameter path +** Parameters: (state_t *) s: The state to set +** (const char *) path: Path where save_dir will point to +** Returns : (void) +*/ + +void +script_free_engine(struct _state *s); +/* Frees all additional memory associated with a state_t block +** Parameters: (state_t *) s: The state_t whose elements should be cleared +** Returns : (void) +*/ + +void +script_free_vm_memory(struct _state *s); +/* Frees all script memory (heap, hunk, and class tables). +** Parameters: (state_t *) s: The state_t to free +** Returns : (void) +** This operation is implicit in script_free_engine(), but is required for restoring +** the game state. +*/ + + +int +lookup_selector(struct _state *s, reg_t obj, selector_t selectorid, reg_t **vptr, reg_t *fptr); +/* Looks up a selector and returns its type and value +** Parameters: (state_t *) s: The state_t to use +** (reg_t) obj: Address of the object to look the selector up in +** (selector_t) selectorid: The selector to look up +** Returns : (int) SELECTOR_NONE if the selector was not found in the object or its superclasses. +** SELECTOR_VARIABLE if the selector represents an object-relative variable +** SELECTOR_METHOD if the selector represents a method +** (reg_t *) *vptr: A pointer to the storage space associated with the selector, if +** it is a variable +** (reg_t) *fptr: A reference to the function described by that selector, if it is +** a valid function selector. +** *vptr is written to iff it is non-NULL and the selector indicates a property of the object. +** *fptr is written to iff it is non-NULL and the selector indicates a member function of that object. +*/ + + +#define SCRIPT_GET_DONT_LOAD 0 /* Fail if not loaded */ +#define SCRIPT_GET_LOAD 1 /* Load, if neccessary */ +#define SCRIPT_GET_LOCK 3 /* Load, if neccessary, and lock */ + +seg_id_t +script_get_segment(struct _state *s, int script_id, int load); +/* Determines the segment occupied by a certain script +** Parameters: (state_t *) s: The state to operate on +** (int) script_id: The script in question +** (int) load: One of SCRIPT_GET_* +** Returns : The script's segment, or 0 on failure +*/ + +reg_t +script_lookup_export(struct _state *s, int script_nr, int export_index); +/* Looks up an entry of the exports table of a script +** Parameters: (state_t *) s: The state to operate on +** (int) script_nr: The script to look up in +** Returns : (int) export_index: index of the export entry to look up +*/ + +int +script_instantiate(struct _state *s, int script_nr); +/* Makes sure that a script and its superclasses get loaded to the heap +** Parameters: (state_t *) s: The state to operate on +** (int) script_nr: The script number to load +** Returns : (int) The script's segment ID or 0 if out of heap +** If the script already has been loaded, only the number of lockers is increased. +** All scripts containing superclasses of this script aret loaded recursively as well, +** unless 'recursive' is set to zero. +** The complementary function is "script_uninstantiate()" below. +*/ + + +void +script_uninstantiate(struct _state *s, int script_nr); +/* Decreases the numer of lockers of a script and unloads it if that number reaches zero +** Parameters: (state_t *) s: The state to operate on +** (int) script_nr: The script number that is requestet to be unloaded +** Returns : (void) +** This function will recursively unload scripts containing its superclasses, if those +** aren't locked by other scripts as well. +*/ + + +int +game_save_state(struct _state *s, char *name, int coredump); +/* Saves the game state to the harddisk +** Parameters: (state_t *) s: The game state to save +** (char *) name: Name of the subdirectory (relative to s->save_dir) +** (int) coredump: Set to non-zero in order to write additional debug information +** Returns : (int) 0 on success, 1 otherwise +*/ + + +struct _state * +game_restore_state(char *name); +/* Restores the game state from a file +** Parameters: (char *) name: Name of the saved game state to restore +** Returns : (state_t *): The restored game state, or NULL on failure +*/ + + +int +game_init(struct _state *s); +/* Initializes an SCI game +** Parameters: (state_t *) s: The state to operate on +** Returns : (int): 0 on success, 1 if an error occured. +** This function must be run before script_run() is executed. +** Graphics data is initialized iff s->gfx_state != NULL. +*/ + +int +game_init_graphics(struct _state *s); +/* Initializes the graphics part of an SCI game +** Parameters: (state_t *) s: The state to initialize the graphics in +** Returns : (int) 0 on success, 1 if an error occured +** This function may only be called if game_init() did not initialize +** the graphics data. +*/ + +int +game_init_sound(struct _state *s, int sound_flags); +/* Initializes the sound part of an SCI game +** Parameters: (state_t *) s: The state to initialize the sound in +** (int) sound_flags: Flags to pass to the sound subsystem +** Returns : (int) 0 on success, 1 if an error occured +** This function may only be called if game_init() did not initialize +** the graphics data. +*/ + + +int +game_run(struct _state **s); +/* Runs an SCI game +** Parameters: (state_t **) s: Pointer to the pointer of the state to operate on +** Returns : (int): 0 on success, 1 if an error occured. +** This is the main function for SCI games. It takes a valid state, loads script 0 to it, +** finds the game object, allocates a stack, and runs the init method of the game object. +** In layman's terms, this runs an SCI game. +** By the way, *s may be changed during the game, e.g. if a game state is restored. +*/ + +int +game_restore(struct _state **s, char *savegame_name); +/* Restores an SCI game state and runs the game +** Parameters: (state_t **) s: Pointer to the pointer of the state to operate on +** (char *) savegame_name: Name of the savegame to restore +** Returns : (int): 0 on success, 1 if an error occured. +** This restores a savegame; otherwise, it behaves just like game_run(). +*/ + +int +game_exit(struct _state *s); +/* Uninitializes an initialized SCI game +** Parameters: (state_t *) s: The state to operate on +** Returns : (int): 0 on success, 1 if an error occured. +** This function should be run after each script_run() call. +*/ + +void +quit_vm(void); +/* Instructs the virtual machine to abort +** Paramteres: (void) +** Returns : (void) +*/ + +void +script_map_selectors(struct _state *s, selector_map_t *map); +/* Maps special selectors +** Parameters: (state_t *) s: The state from which the selector information should be taken +** (selector_map_t *) map: Pointer to the selector map to map +** Returns : (void) +** Called by script_run(); +*/ + +int +script_map_kernel(struct _state *s); +/* Maps kernel functions +** Parameters: (state_t *) s: The state which the kernel_names are retreived from +** Returns : (void) +** This function reads from and writes to s. It is called by script_run(). +*/ + + +void +script_detect_versions(struct _state *s); +/* Detects SCI versions by their different script header +** Parameters: (state_t *) s: The state to operate on +** Returns : (void) +*/ + +reg_t +kalloc(struct _state *s, const char *type, int space); +/* Allocates "kernel" memory and returns a handle suitable to be passed on to SCI scripts +** Parameters: (state_t *) s: Pointer to the state_t to operate on +** (const char *) type: A free-form type description string (static) +** (int) space: The space to allocate +** Returns : (reg_t) The handle +*/ + +int +has_kernel_function(struct _state *s, const char *kname); +/* Detects whether a particular kernel function is required in the game +** Parameters: (state_t *) s: Pointer to the state_t to operate on +** (const char *) kname: The name of the desired kernel function +** Returns : (int) 1 if the kernel function is listed in the kernel table, +** 0 otherwise +*/ + +byte * +kmem(struct _state *s, reg_t handle); +/* Returns a pointer to "kernel" memory based on the handle +** Parameters: (state_t *) s: Pointer to the state_t to operate on +** (reg_t) handle: The handle to use +** Returns : (byte *) A pointer to the allocated memory +*/ + + +int +kfree(struct _state *s, reg_t handle); +/* Frees all "kernel" memory associated with a handle +** Parameters: (state_t *) s: Pointer to the state_t to operate on +** (reg_t) handle: The handle to free +** Returns : (int) 0 on success, 1 otherwise +*/ + +const char * +obj_get_name(struct _state *s, reg_t pos); +/* Determines the name of an object +** Parameters: (state_t *) s: Pointer to the state_t to operate on +** (reg_t) pos: Location of the object whose name we want to +** inspect +** Returns : (const char *) A name for that object, or a string describing +** an error that occured while looking it up +** The string is stored in a static buffer and need not be freed (neither +** may it be modified). +*/ + +object_t * +obj_get(struct _state *s, reg_t offset); +/* Retreives an object from the specified location +** Parameters: (state_t *) s: Pointer to the state_t to operate on +** (reg_t) offset: The object's offset +** Returns : (object_t *) The object in question, or NULL if there is none +*/ + +int +test_savegame(struct _state *s, char *savegame_id, char *savegame_name, int savegame_name_length); +/* Simple savegame validity check +** Parameters: (state_t *) s: Pointer to the state_t to operate on +** (char *) savegame_id: Name of the savegame to check +** (char *) savegame_name: Pointer to a static buffer the savegame +** name string should be stored in +** (int) savegame_name_length: Max. number of bytes to write into the +** static string +** Returns : (int) 1 if it might be a savegame, 0 if not +*/ + +#endif /* !_SCI_VM_H */ diff --git a/engines/sci/include/vm_types.h b/engines/sci/include/vm_types.h new file mode 100644 index 0000000000..66758425ae --- /dev/null +++ b/engines/sci/include/vm_types.h @@ -0,0 +1,71 @@ +/*************************************************************************** + vm_types.h Copyright (C) 2002 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + + +#ifndef _SCI_VM_TYPES_H_ +#define _SCI_VM_TYPES_H_ + +#include + +#define SCI_REG_SIZE 16; +#define SCI_SEG_SIZE 16; + +typedef int seg_id_t; /* Segment ID type */ + +struct _state; /* engine.h */ + +typedef struct { + guint16 segment; + guint16 offset; +} reg_t; + +#define PREG "%04x:%04x" +#define PRINT_REG(r) (0xffff) & (unsigned) (r).segment, (unsigned) (r).offset + +typedef reg_t *stack_ptr_t; /* Stack pointer type */ +typedef int selector_t; /* Selector ID */ +#define NULL_SELECTOR -1 + +#define PSTK "ST:%04x" +#define PRINT_STK(v) (unsigned) (v - s->stack_base) + +static inline reg_t +make_reg(int segment, int offset) +{ + reg_t r; + r.offset = offset; + r.segment = segment; + return r; +} + +#define IS_NULL_REG(r) (!((r).offset || (r).segment)) +#define REG_EQ(a, b) (((a).offset == (b).offset) && ((a).segment == (b).segment)) +#define NULL_REG_INITIALIZER {0, 0} +extern reg_t NULL_REG; + + +#endif /* !_SCI_VM_TYPES_H_ */ diff --git a/engines/sci/include/vocabulary.h b/engines/sci/include/vocabulary.h new file mode 100644 index 0000000000..fb58dd644b --- /dev/null +++ b/engines/sci/include/vocabulary.h @@ -0,0 +1,424 @@ +/*************************************************************************** + vocabulary.h Copyright (C) 1999,2000,01 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + +#ifndef VOCABULARY_H +#define VOCABULARY_H + +#include +#include + +/*#define VOCABULARY_DEBUG */ +/*#define SCI_SIMPLE_SAID_CODE */ /* Whether the simplified Said() matching should be used */ +/*#define SCI_SIMPLE_SAID_DEBUG */ /* uncomment to enable simple said debugging */ + + +#define SCRIPT_UNKNOWN_FUNCTION_STRING "[Unknown]" +/* The string used to identify the "unknown" SCI0 function for each game */ + +#define PARSE_HEAP_SIZE 64 +/* Number of bytes allocated on the heap to store bad words if parsing fails */ + + +typedef struct opcode_ +{ + int type; + int number; + char* name; +} opcode; + +#define VOCAB_RESOURCE_OPCODES 998 +#define VOCAB_RESOURCE_KNAMES 999 + +#define VOCAB_RESOURCE_SCI0_MAIN_VOCAB 0 +#define VOCAB_RESOURCE_SCI0_PARSE_TREE_BRANCHES 900 +#define VOCAB_RESOURCE_SCI0_SUFFIX_VOCAB 901 + +#define VOCAB_RESOURCE_SCI1_MAIN_VOCAB 900 +#define VOCAB_RESOURCE_SCI1_PARSE_TREE_BRANCHES 901 +#define VOCAB_RESOURCE_SCI1_SUFFIX_VOCAB 902 +#define VOCAB_RESOURCE_SCI1_CHAR_TRANSFORMS 913 + +#define VOCAB_CLASS_PREPOSITION 0x01 +#define VOCAB_CLASS_ARTICLE 0x02 +#define VOCAB_CLASS_ADJECTIVE 0x04 +#define VOCAB_CLASS_PRONOUN 0x08 +#define VOCAB_CLASS_NOUN 0x10 +#define VOCAB_CLASS_INDICATIVE_VERB 0x20 +#define VOCAB_CLASS_ADVERB 0x40 +#define VOCAB_CLASS_IMPERATIVE_VERB 0x80 +#define VOCAB_CLASS_NUMBER 0x001 + +extern DLLEXTERN const char *class_names[]; /* Vocabulary class names */ + +#define VOCAB_CLASS_ANYWORD 0xff +/* Anywords are ignored by the parser */ + +#define VOCAB_MAGIC_NUMBER_GROUP 0xffd /* 0xffe ? */ +/* This word class is used for numbers */ + +#define VOCAB_TREE_NODES 500 +/* Number of nodes for each parse_tree_node structure */ + +#define VOCAB_TREE_NODE_LAST_WORD_STORAGE 0x140 +#define VOCAB_TREE_NODE_COMPARE_TYPE 0x146 +#define VOCAB_TREE_NODE_COMPARE_GROUP 0x14d +#define VOCAB_TREE_NODE_FORCE_STORAGE 0x154 + +#define SAID_COMMA 0xf0 +#define SAID_AMP 0xf1 +#define SAID_SLASH 0xf2 +#define SAID_PARENO 0xf3 +#define SAID_PARENC 0xf4 +#define SAID_BRACKO 0xf5 +#define SAID_BRACKC 0xf6 +#define SAID_HASH 0xf7 +#define SAID_LT 0xf8 +#define SAID_GT 0xf9 +#define SAID_TERM 0xff + +#define SAID_FIRST SAID_COMMA + +/* There was no 'last matching word': */ +#define SAID_FULL_MATCH 0xffff +#define SAID_NO_MATCH 0xfffe +#define SAID_PARTIAL_MATCH 0xfffd + +#define SAID_LONG(x) ((x) << 8) + +typedef struct { + + int w_class; /* Word class */ + int group; /* Word group */ + char word[1]; /* The actual word */ + +} word_t; + + +typedef struct { + int id; /* non-terminal ID */ + int first_special; /* first terminal or non-terminal */ + int specials_nr; /* number of terminals and non-terminals */ + int length; + int data[1]; /* actual data (size 1 to avoid compiler warnings) */ +} parse_rule_t; + + +typedef struct _parse_rule_list { + int terminal; /* Terminal character this rule matches against or 0 for a non-terminal rule */ + parse_rule_t *rule; + struct _parse_rule_list *next; +} parse_rule_list_t; + + +typedef struct { + + int class_mask; /* the word class this suffix applies to */ + int result_class; /* the word class a word is morphed to if it doesn't fail this check */ + + int alt_suffix_length; /* String length of the suffix */ + int word_suffix_length; /* String length of the other suffix */ + + char *alt_suffix; /* The alternative suffix */ + char *word_suffix; /* The suffix as used in the word vocabulary */ + +} suffix_t; + + +typedef struct { + + int w_class; /* Word class */ + int group; /* Word group */ + +} result_word_t; + + +typedef struct +{ + int replaceant; /* The word group to replace */ + int replacement; /* The replacement word group for this one */ +} synonym_t; + + +typedef struct { + + int id; + + int data[10]; + +} parse_tree_branch_t; + +#define PARSE_TREE_NODE_LEAF 0 +#define PARSE_TREE_NODE_BRANCH 1 + + +typedef struct { + + short type; /* leaf or branch */ + + union { + + int value; /* For leaves */ + short branches[2]; /* For branches */ + + } content; + +} parse_tree_node_t; + + + + +/*FIXME: These need freeing functions...*/ + +int* vocabulary_get_classes(resource_mgr_t *resmgr, int *count); + +int vocabulary_get_class_count(resource_mgr_t *resmgr); + +/** + * Returns a null terminated array of selector names. + */ +char** vocabulary_get_snames(resource_mgr_t *resmgr, int *pcount, sci_version_t version); + +/** + * Frees the aforementioned array + */ +void vocabulary_free_snames(char **snames_list); + +/* Look up a selector name in an array, return the index */ +int vocabulary_lookup_sname(char **snames_list, char *sname); + + +/** + * Returns a null terminated array of opcodes. + */ +opcode* vocabulary_get_opcodes(resource_mgr_t *resmgr); + +void +vocabulary_free_opcodes(opcode *opcodes); +/* Frees a previously allocated list of opcodes +** Parameters: (opcode *) opcodes: Opcodes to free +** Returns : (void) +*/ + +/** + * Returns a null terminated array of kernel function names. + * + * This function reads the kernel function name table from resource_map, + * and returns a null terminated array of deep copies of them. + * The returned array has the same format regardless of the format of the + * name table of the resource (the format changed between version 0 and 1). + */ +char** vocabulary_get_knames(resource_mgr_t *resmgr, int* count); +void vocabulary_free_knames(char** names); + + + +word_t ** +vocab_get_words(resource_mgr_t *resmgr, int *word_counter); +/* Gets all words from the main vocabulary +** Parameters: (resource_mgr_t *) resmr: The resource manager to read from +** (int *) word_counter: The int which the number of words is stored in +** Returns : (word_t **): A list of all words, dynamically allocated +*/ + + +void +vocab_free_words(word_t **words, int words_nr); +/* Frees memory allocated by vocab_get_words +** Parameters: (word_t **) words: The words to free +** (int) words_nr: Number of words in the structure +** Returns : (void) +*/ + + +suffix_t ** +vocab_get_suffices(resource_mgr_t *resmgr, int *suffices_nr); +/* Gets all suffixes from the suffix vocabulary +** Parameters: (resource_mgr_t*) resmgr: Resource manager the resources are +** read from +** (int *) suffices_nr: The variable to store the number of suffices in +** Returns : (suffix_t **): A list of suffixes +*/ + +void +vocab_free_suffices(resource_mgr_t *resmgr, suffix_t **suffices, int suffices_nr); +/* Frees suffices_nr suffices +** Parameters: (resource_mgr_t *) resmgr: The resource manager to free from +** (suffix_t **) suffices: The suffixes to free +** (int) suffices_nr: Number of entrie sin suffices +** Returns : (void) +*/ + +parse_tree_branch_t * +vocab_get_branches(resource_mgr_t *resmgr, int *branches_nr); +/* Retrieves all grammar rules from the resource data +** Parameters: (resource_mgr_t*) resmgr: Resource manager the rules are +** read from +** (int *) branches_nr: Pointer to the variable which the number of entries is to be +** stored in +** Returns : (parse_tree_branch_t *): The rules, or NULL on error +*/ + +void +vocab_free_branches(parse_tree_branch_t *parser_branches); +/* Frees all branches +** Parameters: (parse_tree_branch_t *) parser_branches: The branches to free +** Returns : (null) +*/ + +result_word_t * +vocab_lookup_word(char *word, int word_len, + word_t **words, int words_nr, + suffix_t **suffices, int suffices_nr); +/* Looks up a single word in the words and suffixes list +** Parameters: (char *) word: Pointer to the word to look up +** (int) word_len: Length of the word to look up +** (word_t **) words: List of words +** (int) words_nr: Number of elements in 'words' +** (suffix_t **) suffices: List of suffices +** (int) suffices_nr: Number of entries in 'suffices' +** Returns : (result_word_t *) A malloc'd result_word_t, or NULL if the word +** could not be found. +*/ + + +result_word_t * +vocab_tokenize_string(char *sentence, int *result_nr, + word_t **words, int words_nr, + suffix_t **suffices, int suffices_nr, + char **error); +/* Tokenizes a string and compiles it into word_ts. +** Parameters: (char *) sentence: The sentence to examine +** (int *) result_nr: The variable to store the resulting number of words in +** (word_t **) words: The words to scan for +** (int) words_nr: Number of words to scan for +** (suffix_t **) suffices: suffixes to scan for +** (int) suffices_nr: Number of suffices to scan for +** (char **) error: Points to a malloc'd copy of the offending text or to NULL on error +** Returns : (word_t *): A list of word_ts containing the result, or NULL. +** On error, NULL is returned. If *error is NULL, the sentence did not contain any useful words; +** if not, *error points to a malloc'd copy of the offending word. +** The returned list may contain anywords. +*/ + + +parse_rule_list_t * +vocab_build_gnf(parse_tree_branch_t *branches, int branches_nr); +/* Constructs the Greibach Normal Form of the grammar supplied in 'branches' +** Parameters: (parse_tree_branch_t *) branches: The parser's branches +** (int) branches_nr: Number of parser branches +** Returns : (parse_rule_list_t *): Pointer to a list of singly linked +** GNF rules describing the same language +** that was described by 'branches' +** The original SCI rules are in almost-CNF (Chomsky Normal Form). Note that +** branch[0] is used only for a few magical incantations, as it is treated +** specially by the SCI parser. +*/ + + +void +vocab_free_rule_list(parse_rule_list_t *rule_list); +/* Frees a parser rule list as returned by vocab_build_gnf() +** Parameters: (parse_rule_list_t *) rule_list: The rule list to free +** Returns : (void) +*/ + + +int +vocab_build_parse_tree(parse_tree_node_t *nodes, result_word_t *words, int words_nr, + parse_tree_branch_t *branch0, parse_rule_list_t *rules); +/* Builds a parse tree from a list of words +** Parameters: (parse_tree_node_t *) nodes: A node list to store the tree in (must have +** at least VOCAB_TREE_NODES entries) +** (result_word_t *) words: The words to build the tree from +** (int) words_nr: The number of words +** (parse_tree_branch_t *) branche0: The zeroeth original branch of the +** original CNF parser grammar +** (parse_rule_list *) rules: The GNF ruleset to parse with +** Returns : 0 on success, 1 if the tree couldn't be built in VOCAB_TREE_NODES nodes +** or if the sentence structure in 'words' is not part of the language +** described by the grammar passed in 'rules'. +*/ + +void +vocab_dump_parse_tree(const char *tree_name, parse_tree_node_t *nodes); +/* Prints a parse tree +** Parameters: (const char *) tree_name: Name of the tree to dump (free-form) +** (parse_tree_node_t *) nodes: The nodes containing the parse tree +** Returns : (void) +*/ + + + + +struct _state; + +int +said(struct _state *s, byte *spec, int verbose); +/* Builds a parse tree from a spec and compares it to a parse tree +** Parameters: (state_t *) s: The affected state +** (byte *) spec: Pointer to the spec to build +** (int) verbose: Whether to display the parse tree after building it +** Returns : (int) 1 on a match, 0 otherwise +*/ + +const char * +vocab_get_any_group_word(int group, word_t **words, int words_nr); +/* Gets any word from the specified group. +** Parameters: (int) group: Group number. +** (word_t **) words: List of words +** (int) words_nr: Count of words in the list. +** For debugging only. +*/ + + +void +vocab_decypher_said_block(struct _state *s, byte *pos); +/* Decyphers a said block and dumps its content via sciprintf. +** Parameters: (state_t *) s: The state to use +** (byte *) pos: Pointer to the data to dump +** For debugging only. +*/ + + +void +vocab_synonymize_tokens(result_word_t *words, int words_nr, synonym_t *synonyms, int synonyms_nr); +/* Synonymizes a token list +** Parameters: (result_wort_t *) words: The word list to synonymize +** (int) words_nr: Number of word_ts in the list +** (synonym_t *) synonyms: Synonym list +** (int) synonyms_nr: Number of synonyms in the list +*/ + +int +vocab_gnf_parse(parse_tree_node_t *nodes, result_word_t *words, int words_nr, + parse_tree_branch_t *branch0, parse_rule_list_t *tlist, int verbose); + +void +vocab_gnf_dump(parse_tree_branch_t *branches, int branches_nr); + + +#endif diff --git a/engines/sci/include/win32/Makefile.am b/engines/sci/include/win32/Makefile.am new file mode 100644 index 0000000000..89551871e5 --- /dev/null +++ b/engines/sci/include/win32/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = getopt.h sci_win32.h usleep.h diff --git a/engines/sci/include/win32/getopt.h b/engines/sci/include/win32/getopt.h new file mode 100644 index 0000000000..0abce6e921 --- /dev/null +++ b/engines/sci/include/win32/getopt.h @@ -0,0 +1,127 @@ +/* Declarations for getopt. + Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if __STDC__ || defined(PROTO) +#if defined(__GNU_LIBRARY__) +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +#endif /* not __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ +extern int getopt (); +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +#endif /* not __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ diff --git a/engines/sci/include/win32/sci_win32.h b/engines/sci/include/win32/sci_win32.h new file mode 100644 index 0000000000..5174fd101c --- /dev/null +++ b/engines/sci/include/win32/sci_win32.h @@ -0,0 +1,37 @@ +#ifdef _MSC_VER +// inline keyword only supported in C++ +# undef inline /* just to be sure it is not defined */ +# define inline __inline +# define strcasecmp _stricmp +# define strncasecmp _strnicmp +# define snprintf _snprintf + +# if _MSC_VER < 1500 +# define vsnprintf _vsnprintf +# endif +#endif + +#ifdef _WIN32 + +# ifdef sleep +# undef sleep +# endif + +# define sleep(x) \ + do { \ + if (x == 0) { \ + Sleep(0); \ + } else { \ + if (timeBeginPeriod(1) != TIMERR_NOERROR) \ + fprintf(stderr, "timeBeginPeriod(1) failed\n"); \ + Sleep(x); \ + if (timeEndPeriod(1) != TIMERR_NOERROR) \ + fprintf(stderr, "timeEndPeriod(1) failed\n"); \ + } \ + } while (0); + +# define RECT_T_TO_RECT(rect_t) \ + { (rect_t).x, (rect_t).y, (rect_t).x + (rect_t).xl, (rect_t).y + (rect_t).yl } + +#endif + diff --git a/engines/sci/include/win32/usleep.h b/engines/sci/include/win32/usleep.h new file mode 100644 index 0000000000..dd85d3cd87 --- /dev/null +++ b/engines/sci/include/win32/usleep.h @@ -0,0 +1,2 @@ +void +usleep (long usec); diff --git a/engines/sci/main.c b/engines/sci/main.c new file mode 100644 index 0000000000..2da6a550be --- /dev/null +++ b/engines/sci/main.c @@ -0,0 +1,1840 @@ +/*************************************************************************** + main.c Copyright (C) 1999,2000,01,02 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "list.h" +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_FORK +# include +#endif + +#if defined(HAVE_SDL) && defined(MACOSX) +# include +/* On OS X, SDL must #define main to something else in order to function */ +#endif + +#ifdef _MSC_VER +#define extern __declspec(dllimport) extern +#include +#endif + +#ifdef HAVE_READLINE_READLINE_H +#include +#ifdef HAVE_READLINE_HISTORY_H +#include +#endif /* HAVE_READLINE_HISTORY_H */ +#endif /* HAVE_READLINE_READLINE_H */ + +#ifdef HAVE_GETOPT_H +# ifndef _MSC_VER +# include +# else +# include +# endif +#endif /* HAVE_GETOPT_H */ + +#ifdef HAVE_GETOPT_LONG +#define EXPLAIN_OPTION(longopt, shortopt, description) " " longopt "\t" shortopt "\t" description "\n" +#else /* !HAVE_GETOPT_H */ +#define EXPLAIN_OPTION(longopt, shortopt, description) " " shortopt "\t" description "\n" +#endif /* !HAVE_GETOPT_H */ + + +#ifdef _WIN32 +# ifdef _MSC_VER +# include +# define PATH_MAX 255 +# endif +# define WIN32_LEAN_AND_MEAN +# include +#endif + +#ifdef _DREAMCAST +# include +#endif + +#ifdef _MSC_VER +# define MSVC_FUNCTYPECAST_KLUDGE (void *) +#else +# define MSVC_FUNCTYPECAST_KLUDGE +#endif + +#define ACTION_PLAY 0 +#define ACTION_LIST_SAVEGAMES 1 + +static int sciv_action = ACTION_PLAY; + +/*** HW/OS-dependant features ***/ + +static void +check_features() +{ +#ifdef HAVE_ALPHA_EV6_SUPPORT + int helper; + printf("Checking for MVI instruction-set extension: "); + + helper = 0x100; +#ifdef __DECC + axp_have_mvi = asm("amask %0, %v0", helper); +#else + __asm__ ("amask %1, %0" + : "=r"(axp_have_mvi) + : "r"(helper)); +#endif + + axp_have_mvi = !axp_have_mvi; + + if (axp_have_mvi) + printf("found\n"); + else + printf("not present\n"); +#endif +} + + +static gfx_state_t static_gfx_state; /* see below */ +static gfx_options_t static_gfx_options; /* see below */ + +static state_t *gamestate; /* The main game state */ +static gfx_state_t *gfx_state = &static_gfx_state; /* The graphics state */ +static gfx_options_t *gfx_options = &static_gfx_options; /* Graphics options */ +static char *commandline_config_file = NULL; + +int +c_quit(state_t *s) +{ + script_abort_flag = 1; /* Terminate VM */ + _debugstate_valid = 0; + _debug_seeking = 0; + _debug_step_running = 0; + return 0; +} + +int +c_die(state_t *s) +{ + exit(0); /* Die */ + return 0; /* ;-P (fixes warning) */ +} + + +char *old_input = NULL; + +#ifdef HAVE_READLINE_READLINE_H +const char * +get_readline_input(void) +{ + char *input; + + fflush(NULL); + input = readline("> "); + + if (!input) { /* ^D */ + c_quit(NULL); + return ""; + } + + if (strlen(input) == 0) { + free (input); + } else { + +#ifdef HAVE_READLINE_HISTORY_H + add_history(input); +#endif /* HAVE_READLINE_HISTORY_H */ + + if (old_input) { + free(old_input); + } + old_input = input; + } + + return old_input? old_input : ""; +} +#endif /* HAVE_READLINE_READLINE_H */ + + +int +init_directories(char *work_dir, char *game_id) +{ + char *homedir = sci_get_homedir(); + + printf("Initializing directories...\n"); + if (!homedir) { /* We're probably not under UNIX if this happens */ + + if (!getcwd(work_dir, PATH_MAX)) { + fprintf(stderr,"Cannot get the working directory!\n"); + return 1; + } + + return 0; + } + + /* So we've got a home directory */ + + if (chdir(homedir)) { + +#ifdef _WIN32 + if (!getcwd(work_dir, PATH_MAX)) { + fprintf(stderr,"Cannot get the working directory: %s\n", work_dir); + return 1; + } +#else /* Assume UNIX-ish environment */ + fprintf(stderr,"Error: Could not enter home directory %s.\n", homedir); + perror("Reason"); + return 1; /* If we get here, something really bad is happening */ +#endif + } + + if (strlen(homedir) > MAX_HOMEDIR_SIZE) { + fprintf(stderr, "Your home directory path is too long. Re-compile FreeSCI with " + "MAX_HOMEDIR_SIZE set to at least %i and try again.\n", (int)(strlen(homedir))); + return 1; + } + + if (chdir(FREESCI_GAMEDIR)) { + if (scimkdir(FREESCI_GAMEDIR, 0700)) { + + fprintf(stderr, "Warning: Could not enter ~/"FREESCI_GAMEDIR"; save files" + " will be written to ~/\n"); + + getcwd(work_dir, PATH_MAX); + return 0; + + } + else /* mkdir() succeeded */ + chdir(FREESCI_GAMEDIR); + } + + if (chdir(game_id)) { + if (scimkdir(game_id, 0700)) { + + fprintf(stderr,"Warning: Could not enter ~/"FREESCI_GAMEDIR"/%s; " + "save files will be written to ~/"FREESCI_GAMEDIR"\n", game_id); + + getcwd(work_dir, PATH_MAX); + return 0; + } + else /* mkdir() succeeded */ + chdir(game_id); + } + + getcwd(work_dir, PATH_MAX); + + return 0; +} + + +const char * +get_gets_input(void) +{ + static char input[1024] = ""; + + putchar('>'); + + fflush(NULL); + while (!strchr(input, '\n')) + fgets(input, 1024, stdin); + + if (!input) { + c_quit(NULL); + return ""; + } + + if (strlen(input)) + if (input[strlen(input)-1] == '\n'); + input[strlen(input)-1] = 0; /* Remove trailing '\n' */ + + if (strlen(input) == 0) { + return old_input? old_input : ""; + } + + if (old_input) + free(old_input); + + old_input = (char *) sci_malloc(1024); + strcpy(old_input, input); + return input; +} + + + + +static void +list_graphics_drivers() +{ + int i = 0; + while (gfx_get_driver_name(i)) { + if (i != 0) + printf(", "); + + printf(gfx_get_driver_name(i)); + + i++; + } + printf("\n"); +} + +#ifdef __GNUC__ +#warning "Re-enable sound stuff" +#endif +#if 0 +static void +list_pcmout_drivers() +{ + int i = 0; + while (pcmout_drivers[i]) { + if (i != 0) + printf(", "); + printf(pcmout_drivers[i]->name); + i++; + } + printf("\n"); +} + +static void +list_midiout_drivers() +{ + int i = 0; + while (midiout_drivers[i]) { + if (i != 0) + printf(", "); + printf(midiout_drivers[i]->name); + i++; + } + printf("\n"); +} + + +static void +list_midi_devices() +{ + int i = 0; + while (midi_devices[i]) { + if (i != 0) + printf(", "); + printf(midi_devices[i]->name); + i++; + } + printf("\n"); +} + +static void +list_sound_servers() +{ + int i = 0; + while (sound_servers[i]) { + if (i != 0) + printf(", "); + printf(sound_servers[i]->name); + i++; + } + printf("\n"); +} +#endif + + +/**********************************************************/ +/* Startup and config management */ +/**********************************************************/ + +typedef struct { + int script_debug_flag; + int scale_x, scale_y, color_depth; + int mouse; + int master_sound; /* on or off */ + int show_rooms; + sci_version_t version; + int res_version; + char *gfx_driver_name; + char *gamedir; + char *gamemenu; + char *midiout_driver_name; + char *midi_device_name; + char *sound_server_name; + char *pcmout_driver_name; +} cl_options_t; + +#define ON 1 +#define OFF 0 +#define DONTCARE -1 + +static int game_select(cl_options_t cl_options, config_entry_t *confs, int conf_entries, const char* freesci_dir, const char *games_dir); +static int game_select_resource_found(); + +static char * +parse_arguments(int argc, char **argv, cl_options_t *cl_options, char **savegame_name) +{ + int c; +#ifdef HAVE_GETOPT_LONG + int optindex; + + struct option options[] = { + {"run", no_argument, NULL, 0 }, + {"debug", no_argument, NULL, 1 }, + {"gamedir", required_argument, 0, 'd'}, + {"menudir", required_argument, 0, 'G'}, + {"no-sound", required_argument, 0, 'q'}, + {"sci-version", required_argument, 0, 'V'}, + {"graphics", required_argument, 0, 'g'}, + {"midiout", required_argument, 0, 'O'}, + {"pcmout", required_argument, 0, 'P'}, + {"sound-server", required_argument, 0, 'S'}, + {"mididevice", required_argument, 0, 'M'}, + {"version", no_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {"scale-x", required_argument, 0, 'x'}, + {"scale-y", required_argument, 0, 'y'}, + {"color-depth", required_argument, 0, 'c'}, + {"disable-mouse", no_argument, 0, 'm'}, + {"list-savegames", no_argument, 0, 'l'}, + {"show-rooms", no_argument, 0, 's'}, + {"config-file", required_argument, 0, 'f'}, + {0,0,0,0} + }; + + options[0].flag = &(cl_options->script_debug_flag); + options[1].flag = &(cl_options->script_debug_flag); +#endif /* HAVE_GETOPT_H */ + + cl_options->scale_x = cl_options->scale_y = cl_options->color_depth = 0; + cl_options->version = 0; + cl_options->script_debug_flag = 0; + cl_options->gfx_driver_name = NULL; + cl_options->gamedir = NULL; + cl_options->gamemenu = NULL; + cl_options->midiout_driver_name = NULL; + cl_options->pcmout_driver_name = NULL; + cl_options->midi_device_name = NULL; + cl_options->sound_server_name = NULL; + cl_options->mouse = ON; + cl_options->master_sound = ON; + cl_options->res_version = SCI_VERSION_AUTODETECT; + cl_options->show_rooms = 0; + +#ifdef HAVE_GETOPT_LONG + while ((c = getopt_long(argc, argv, "qlvhmsDr:d:G:V:g:x:y:c:M:O:S:P:f:", options, &optindex)) > -1) { +#else /* !HAVE_GETOPT_LONG */ + while ((c = getopt(argc, argv, "qlvhmsDr:d:G:V:g:x:y:c:M:O:S:P:f:")) > -1) { +#endif /* !HAVE_GETOPT_LONG */ + switch (c) { + + case 'r': + cl_options->res_version = atoi(optarg); + break; + + case 's': + cl_options->show_rooms = 1; + break; + + case 'D': + cl_options->script_debug_flag = 1; + break; + + case 'd': + if (cl_options->gamedir) + free(cl_options->gamedir); + + cl_options->gamedir = sci_strdup(optarg); + break; + + case 'G': + if (cl_options->gamemenu) + free(cl_options->gamemenu); + + cl_options->gamemenu = sci_strdup(optarg); + break; + + case 'f': + commandline_config_file = sci_strdup(optarg); + break; + + case 'V': { + int major = *optarg - '0'; /* One version digit */ + int minor = atoi(optarg + 2); + int patchlevel = atoi(optarg + 6); + + cl_options->version = SCI_VERSION(major, minor, patchlevel); + } + break; + + case 'g': + if (cl_options->gfx_driver_name) + free(cl_options->gfx_driver_name); + cl_options->gfx_driver_name = sci_strdup(optarg); + break; + case 'O': + if (cl_options->midiout_driver_name) + free(cl_options->midiout_driver_name); + cl_options->midiout_driver_name = sci_strdup(optarg); + break; + case 'P': + if (cl_options->pcmout_driver_name) + free(cl_options->pcmout_driver_name); + cl_options->pcmout_driver_name = sci_strdup(optarg); + break; + case 'M': + if (cl_options->midi_device_name) + free(cl_options->midi_device_name); + cl_options->midi_device_name = sci_strdup(optarg); + break; + case 'S': + if (cl_options->sound_server_name) + free(cl_options->sound_server_name); + cl_options->sound_server_name = sci_strdup(optarg); + break; + case '?': + /* getopt_long already printed an error message. */ + exit(1); + + case 'x': + cl_options->scale_x = atoi(optarg); + break; + + case 'y': + cl_options->scale_y = atoi(optarg); + break; + + case 'c': + cl_options->color_depth = (atoi(optarg) +7) >> 3; + break; + + case 'm': + cl_options->mouse = OFF; + break; + + case 'q': + cl_options->master_sound = OFF; + break; + + case 0: /* getopt_long already did this for us */ + break; + + case 'v': + printf("This is FreeSCI, version %s\n", VERSION); + + printf("Supported graphics drivers: "); + list_graphics_drivers(); + +#ifdef __GNUC__ +#warning "Re-enable sound stuff" +#endif +#if 0 + printf("Supported sound servers: "); + list_sound_servers(); + + printf("Supported midiout drivers: "); + list_midiout_drivers(); + + printf("Supported midi 'devices': "); + list_midi_devices(); + + printf("Supported pcmout drivers: "); + list_pcmout_drivers(); +#endif + + printf("\n"); + exit(0); + + case 'h': + printf("Usage: freesci [options] [game name] [savegame ID]\n" + "Runs a Sierra SCI game.\n" + "\n" + EXPLAIN_OPTION("--gamedir dir\t", "-ddir", "read game resources from dir") + EXPLAIN_OPTION("--menudir dir\t", "-Gdir", "display menu for all games under dir") + EXPLAIN_OPTION("--run\t\t", "-r", "do not start the debugger") + EXPLAIN_OPTION("--sci-version ver", "-Vver", "set the version for freesci to emulate") + EXPLAIN_OPTION("--version\t", "-v", "display version number and exit") + EXPLAIN_OPTION("--debug\t", "-D", "start up in debug mode") + EXPLAIN_OPTION("--help\t", "-h", "display this help text and exit") + EXPLAIN_OPTION("--graphics gfx", "-ggfx", "use the 'gfx' graphics driver") + EXPLAIN_OPTION("--scale-x\t", "-x", "Set horizontal scale factor") + EXPLAIN_OPTION("--scale-y\t", "-y", "Set vertical scale factor") + EXPLAIN_OPTION("--color-depth\t", "-c", "Specify color depth in bpp") + EXPLAIN_OPTION("--disable-mouse", "-m", "Disable support for pointing device") + EXPLAIN_OPTION("--midiout drv\t", "-Odrv", "use the 'drv' midiout driver") + EXPLAIN_OPTION("--mididevice drv", "-Mdrv", "use the 'drv' midi device (eg mt32 or adlib)") + EXPLAIN_OPTION("--pcmout drv\t", "-Pdrv", "use the 'drv' pcmout driver") + EXPLAIN_OPTION("--sound-server srv", "-Ssrv", "Specifies the asynchronous sound server to use") + EXPLAIN_OPTION("--no-sound\t", "-q", "disable sound output") + EXPLAIN_OPTION("--list-savegames", "-l", "Lists all savegame IDs") + EXPLAIN_OPTION("--show-rooms\t", "-s","Displays room numbers on the game console") + "\n" + "The game name, if provided, must be equal to a game name as specified in the\n" + "FreeSCI config file.\n" + "It is overridden by --gamedir.\n" + "\n" + ); + exit(0); + + case 'l': + sciv_action = ACTION_LIST_SAVEGAMES; + break; + + default: + exit(1); + } + } +#if 0 + } /* Work around EMACS paren matching bug */ +#endif + + if (optind+1 >= argc) + *savegame_name = NULL; + else + *savegame_name = argv[optind + 1]; + + if (optind == argc) + return NULL; + + return + argv[optind]; +} + +static int +find_config(char *game_name, config_entry_t *conf, int conf_entries, + sci_version_t *version) +{ + int i, conf_nr = 0; + + for (i = 1; i < conf_entries; i++) + if (!strcasecmp(conf[i].name, game_name)) { + conf_nr = i; + if (version) + *version = conf[i].version; + } + + return conf_nr; +} + +static void +init_console() +{ +#ifdef WANT_CONSOLE + con_gfx_init(); +#endif + con_hook_command(&c_quit, "quit", "", "console: Quits gracefully"); + con_hook_command(&c_die, "die", "", "console: Quits ungracefully"); + + con_hook_int(&(gfx_options->buffer_pics_nr), "buffer_pics_nr", + "Number of pics to buffer in LRU storage\n"); + con_hook_int(&(gfx_options->pic0_dither_mode), "pic0_dither_mode", + "Mode to use for pic0 dithering\n"); + con_hook_int(&(gfx_options->pic0_dither_pattern), "pic0_dither_pattern", + "Pattern to use for pic0 dithering\n"); + con_hook_int(&(gfx_options->pic0_unscaled), "pic0_unscaled", + "Whether pic0 should be drawn unscaled\n"); + con_hook_int(&(gfx_options->dirty_frames), "dirty_frames", + "Dirty frames management\n"); + con_hook_int(&gfx_crossblit_alpha_threshold, "alpha_threshold", + "Alpha threshold for crossblitting\n"); + con_hook_int(&sci0_palette, "sci0_palette", + "SCI0 palette- 0: EGA, 1:AGI/Amiga, 2:Grayscale\n"); + con_hook_int(&sci01_priority_table_flags, "sci01_priority_table_flags", + "SCI01 priority table debugging flags: 1:Disable, 2:Print on change\n"); + + con_passthrough = 1; /* enables all sciprintf data to be sent to stdout */ + +#ifdef HAVE_READLINE_HISTORY_H + using_history(); /* Activate history for readline */ +#endif /* HAVE_READLINE_HISTORY_H */ + +#ifdef HAVE_READLINE_READLINE_H + _debug_get_input = get_readline_input; /* Use readline for debugging input */ +#else /* !HAVE_READLINE_READLINE_H */ + _debug_get_input = get_gets_input; /* Use gets for debug input */ +#endif /* !HAVE_READLINE_READLINE_H */ +} + + +static int +init_gamestate(state_t *gamestate, resource_mgr_t *resmgr, sci_version_t version) +{ + int errc; + gamestate->resmgr = resmgr; + + if ((errc = script_init_engine(gamestate, version))) { /* Initialize game state */ + int recovered = 0; + + if (errc == SCI_ERROR_INVALID_SCRIPT_VERSION) { + int tversion = SCI_VERSION_FTU_NEW_SCRIPT_HEADER - ((version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER)? 0 : 1); + + while (!recovered && tversion) { + printf("Trying version %d.%03x.%03d instead\n", SCI_VERSION_MAJOR(tversion), + SCI_VERSION_MINOR(tversion), SCI_VERSION_PATCHLEVEL(tversion)); + + errc = script_init_engine(gamestate, tversion); + + if ((recovered = !errc)) + version = tversion; + + if (errc != SCI_ERROR_INVALID_SCRIPT_VERSION) + break; + + switch (tversion) { + + case SCI_VERSION_FTU_NEW_SCRIPT_HEADER - 1: + if (version >= SCI_VERSION_FTU_NEW_SCRIPT_HEADER) + tversion = 0; + else + tversion = SCI_VERSION_FTU_NEW_SCRIPT_HEADER; + break; + + case SCI_VERSION_FTU_NEW_SCRIPT_HEADER: + tversion = 0; + break; + } + } + if (recovered) + printf("Success.\n"); + } + + if (!recovered) { + fprintf(stderr,"Script initialization failed. Aborting...\n"); + return 1; + } + } + return 0; +} + +static int +init_gfx(config_entry_t *conf, cl_options_t *cl_options, gfx_driver_t *driver, resource_mgr_t *resmgr) +{ + int scale_x = 0, scale_y = 0, color_depth = 0; + + if (conf) { + if (conf->scale) + scale_x = scale_y = conf->scale; + + if (conf->x_scale) + scale_x = conf->x_scale; + + if (conf->y_scale) + scale_y = conf->y_scale; + + if (conf->color_depth) + color_depth = conf->color_depth >> 3; /* In there it's bpp */ + } + + gfx_state->driver = driver; + gamestate->gfx_state = gfx_state; + gfx_state->version = resmgr->sci_version; + + if (cl_options->scale_x > 0) { + scale_x = cl_options->scale_x; + + if (!scale_y) + scale_y = cl_options->scale_x; + } + + if (cl_options->scale_y > 0) { + scale_y = cl_options->scale_y; + + if (!scale_x) + scale_x = cl_options->scale_y; + } + + if (cl_options->color_depth > 0) + color_depth = cl_options->color_depth; + + if (cl_options->color_depth > 0 && scale_x == 0) + scale_x = scale_y = 2; /* Some default setting */ + + if (scale_x > 0) { + + if (color_depth > 0) { + if (gfxop_init(gfx_state, scale_x, + scale_y, (gfx_color_mode_t) color_depth, + gfx_options, resmgr)) { + fprintf(stderr,"Graphics initialization failed. Aborting...\n"); + return 1; + } + } else { + color_depth = 4; + while (gfxop_init(gfx_state, scale_x, + scale_y, (gfx_color_mode_t) color_depth, + gfx_options, resmgr) && --color_depth); + + if (!color_depth) { + fprintf(stderr,"Could not find a matching color depth. Aborting...\n"); + return 1; + } + } + + } else if (gfxop_init_default(gfx_state, gfx_options, resmgr)) { + fprintf(stderr,"Graphics initialization failed. Aborting...\n"); + return 1; + } + + return 0; +} + + +typedef void *old_lookup_funct_t(char *name); + +typedef void *lookup_funct_t(const char *path, const char *name); + + +static void * +lookup_driver(lookup_funct_t lookup_func, void explain_func(void), + const char *driver_class, const char *driver_name, const char *path) +{ + void *retval = lookup_func(path, driver_name); + + if (!retval) { + if (!driver_name) + sciprintf("The default %s is not available; please choose" + " one explicitly.\n", driver_class); + else + sciprintf("The %s you requested, '%s', is not available.\n" +/* "Please choose one among the following: " */ + , + driver_class, driver_name); +/* explain_func(); */ + exit(1); + } + + return retval; +} + + +/*static void * +old_lookup_driver(old_lookup_funct_t lookup_func, void explain_func(void), + char *driver_class, char *driver_name) +{ + void *retval = lookup_func(driver_name); + + if (!retval) { + sciprintf("The %s you requested, '%s', is not available.\n" + "Please choose one among the following: ", + driver_class, driver_name); + explain_func(); + exit(1); + } + + return retval; +}*/ + +#define NAMEBUF_LEN 30 +static void +list_savegames(state_t *s) +{ + sci_dir_t dir; + char *filename = NULL; + + sci_init_dir(&dir); + + filename = sci_find_first(&dir, "*"); + + sciprintf("\nSavegame listing:\n" + "-----------------\n"); + while (filename) { + char namebuf[NAMEBUF_LEN + 1]; + if (test_savegame(s, filename, namebuf, NAMEBUF_LEN)) { + if (namebuf[0]) + sciprintf("%s:\t\"%s\"\n", filename, namebuf); + else + sciprintf("%s\n", filename); + } + filename = sci_find_next(&dir); + } + sciprintf("-----------------\n"); +} + +void +get_file_directory(char* directory, const char* file) +{ + char* end; + + strcpy(directory, file); + + end = directory + strlen(directory) - 1; + while ((end >= directory) && (end != 0)) + { + if (*end == G_DIR_SEPARATOR) + { + *end = 0; + break; + } + else + { + end--; + } + } +} + +static void +detect_versions(sci_version_t *version, int *res_version, cl_options_t *options, config_entry_t *conf) +{ + sci_version_t exe_version; + sci_version_t hash_version; + int hash_res_version; + guint32 code; + int got_exe_version; + const char *game_name; + + sciprintf("Detecting interpreter and resource versions...\n"); + + got_exe_version = !version_detect_from_executable(&exe_version); + + if (got_exe_version) { + sciprintf("Interpreter version: %d.%03d.%03d (by executable scan)\n", + SCI_VERSION_MAJOR(exe_version), + SCI_VERSION_MINOR(exe_version), + SCI_VERSION_PATCHLEVEL(exe_version)); + + if (SCI_VERSION_MAJOR(exe_version) >= 1) { + sciprintf("FIXME: Implement version mapping (results of executable scan ignored)\n"); + got_exe_version = 0; + } + + } + + game_name = version_guess_from_hashcode(&hash_version, &hash_res_version, &code); + + if (game_name) { + sciprintf("Interpreter version: %d.%03d.%03d (by hash code %08X)\n", + SCI_VERSION_MAJOR(hash_version), + SCI_VERSION_MINOR(hash_version), + SCI_VERSION_PATCHLEVEL(hash_version), code); + if (got_exe_version && exe_version != hash_version) + sciprintf("UNEXPECTED INCONSISTENCY: Hash code %08X indicates interpreter version\n" + " %d.%03d.%03d, but analysis of the executable yields %d.%03d.%03d (for game\n" + " '%s'). Please report this!\n", + code, + SCI_VERSION_MAJOR(hash_version), + SCI_VERSION_MINOR(hash_version), + SCI_VERSION_PATCHLEVEL(hash_version), + SCI_VERSION_MAJOR(exe_version), + SCI_VERSION_MINOR(exe_version), + SCI_VERSION_PATCHLEVEL(exe_version), game_name); + + if (hash_res_version != SCI_VERSION_AUTODETECT) + sciprintf("Resource version: %d (by hash code)\n", hash_res_version); + + sciprintf("Game identified as '%s'\n", game_name); + } else { + sciprintf("Could not identify game by hash code: %08X\n", code); + + if (got_exe_version) + sciprintf("Please report the preceding two lines and the name of the game you were trying\n" + "to run to the FreeSCI development team to help other users!\n", + code); + } + + if (options->version) + *version = options->version; + else if (conf && conf->version) + *version = conf->version; + else if (game_name) + *version = hash_version; + else if (got_exe_version) + *version = exe_version; + else + *version = 0; + + if (options->res_version != SCI_VERSION_AUTODETECT) + *res_version = options->res_version; + else if (conf && conf->res_version != SCI_VERSION_AUTODETECT) + *res_version = conf->res_version; + else if (game_name) + *res_version = hash_res_version; + else + *res_version = SCI_VERSION_AUTODETECT; + + if (*version) + sciprintf("Using interpreter version %d.%03d.%03d\n", + SCI_VERSION_MAJOR(*version), + SCI_VERSION_MINOR(*version), + SCI_VERSION_PATCHLEVEL(*version)); + + if (*res_version != SCI_VERSION_AUTODETECT) + sciprintf("Using resource version %d\n", *res_version); +} + +int +main(int argc, char** argv) +{ + config_entry_t *active_conf = NULL; /* Active configuration used */ + config_entry_t *confs = {0}; /* Configuration read from config file (if it exists) */ + cl_options_t cl_options; /* Command line options */ + int conf_entries = -1; /* Number of config entries */ + int conf_nr = -1; /* Element of conf to use */ +/* FILE *console_logfile = NULL; */ + char freesci_dir[PATH_MAX+1] = ""; + char startdir[PATH_MAX+1] = ""; + char resource_dir[PATH_MAX+1] = ""; + char work_dir[PATH_MAX+1] = ""; + char *cwd; + char *gfx_driver_name = NULL; +/* char *midiout_driver_name = NULL; + char *midi_device_name = NULL; + char *pcm_driver_name = NULL; */ + char *game_name = NULL; + char *savegame_name = NULL; + sci_version_t version; + int res_version; + gfx_driver_t *gfx_driver = NULL; +#if 0 + sound_server_t *sound_server = NULL; +#endif + const char *module_path = SCI_DEFAULT_MODULE_PATH; + resource_mgr_t *resmgr; +#ifdef _DREAMCAST + /* Fake command line arguments. */ + char *args[] = {"/cd/freesci.bin", "-f/ram/config", NULL}; + argv = args; + argc = 2; + chdir("/ram"); +#endif + + init_console(); /* So we can get any output */ + + game_name = parse_arguments(argc, argv, &cl_options, &savegame_name); + + /* remember where freesci executable is located */ + get_file_directory(freesci_dir, argv[0]); + + getcwd(startdir, PATH_MAX); + script_debug_flag = cl_options.script_debug_flag; + + printf("FreeSCI %s Copyright (C) 1999-2007\n", VERSION); + printf(" Dmitry Jemerov, Christopher T. Lansdown, Sergey Lapin, Rickard Lind,\n" + " Carl Muckenhoupt, Christoph Reichenbach, Magnus Reftel, Lars Skovlund,\n" + " Rink Springer, Petr Vyhnak, Solomon Peachy, Matt Hargett, Alex Angas\n" + " Walter van Niftrik, Rainer Canavan, Ruediger Hanke, Hugues Valois\n" + "This program is free software. You can copy and/or modify it freely\n" + "according to the terms of the GNU general public license, v2.0\n" + "or any later version, at your option.\n" + "It comes with ABSOLUTELY NO WARRANTY.\n"); + +#ifdef _DREAMCAST + choose_game(); + game_name = "game"; +#endif + + conf_entries = config_init(&confs, commandline_config_file); + + /* working directory was changed by config_init so restore it */ + chdir(startdir); + + if (game_name) { + + conf_nr = find_config(game_name, confs, conf_entries, &version); + active_conf = confs + conf_nr; + + if (!cl_options.gamedir) + if (chdir(active_conf->resource_dir)) { + if (conf_nr) + fprintf(stderr,"Error entering '%s' to load resource data\n", active_conf->resource_dir); + else + fprintf(stderr,"Game '%s' isn't registered in your config file.\n", game_name); + exit(1); + } + } + + if (cl_options.gamedir) + { + if (chdir(cl_options.gamedir)) { + printf ("Error changing to game directory '%s'\n", cl_options.gamedir); + exit(1); + } + free(cl_options.gamedir); + } + + /* by now, if the user specified a game name or a game directory, the working dir has been changed */ + /* so if no resource are found in the working dir, invoke the game selection screen */ + if (!game_name && !game_select_resource_found()) + { + char *menu_dir; + + if (cl_options.gamemenu) + menu_dir = cl_options.gamemenu; + else + menu_dir = confs->menu_dir; + + chdir(startdir); + conf_nr = game_select(cl_options, confs, conf_entries, freesci_dir, menu_dir); + if (conf_nr < 0) + return 1; + if (conf_nr > 0) + /* A game from the config file was chosen */ + active_conf = confs + conf_nr; + } + + detect_versions(&version, &res_version, &cl_options, active_conf); + + getcwd(resource_dir, PATH_MAX); /* Store resource directory */ + + sciprintf("Loading resources...\n"); + + resmgr = scir_new_resource_manager(resource_dir, res_version, 1, 256*1024); + + if (!resmgr) { + printf("No resources found in '%s'.\nAborting...\n", + resource_dir); + exit(1); + } + + script_adjust_opcode_formats(resmgr->sci_version); + + check_features(); + + chdir(startdir); + +#ifdef __GNUC__ +#warning "sound" +#endif +#if 0 + printf("Mapping instruments to General Midi\n"); + + map_MIDI_instruments(resmgr); +#endif + + sciprintf("FreeSCI, version "VERSION"\n"); + + gamestate = (state_t *) sci_calloc(sizeof(state_t), 1); + + if (init_gamestate(gamestate, resmgr, version)) + return 1; + + gamestate->gfx_state = NULL; + if (game_init(gamestate)) { /* Initialize */ + fprintf(stderr,"Game initialization failed: Aborting...\n"); + return 1; + } + + if (init_directories(work_dir, (char *) gamestate->game_name)) { + fprintf(stderr,"Error resolving the working directory\n"); + exit(1); + } + + /* Set the CWD as the savegame dir */ + cwd = sci_getcwd(); + script_set_gamestate_save_dir(gamestate, cwd); + sci_free(cwd); + + if (sciv_action == ACTION_LIST_SAVEGAMES) { + list_savegames(gamestate); + exit(0); + } + gamestate->resource_dir = resource_dir; + gamestate->work_dir = work_dir; + gamestate->port_serial = 0; + + if (!game_name) + game_name = (char *) gamestate->game_name; + + /* If no game-specific configuration has been read, then read the non-specific config from file */ + if (!active_conf) { + conf_nr = find_config(game_name, confs, conf_entries, &version); + active_conf = confs + conf_nr; + } + + /* gcc doesn't warn about (void *)s being typecast. If your compiler doesn't like these + ** implicit casts, don't hesitate to typecast appropriately. */ + if (cl_options.gfx_driver_name) { + gfx_driver_name = sci_strdup(cl_options.gfx_driver_name); + free(cl_options.gfx_driver_name); + } /* else it's still NULL */ + +#ifdef __GNUC__ +#warning "sound" +#endif +#if 0 + if (cl_options.pcmout_driver_name) + pcmout_driver = old_lookup_driver((old_lookup_funct_t *)pcmout_find_driver, + MSVC_FUNCTYPECAST_KLUDGE list_pcmout_drivers, + "pcmout driver", cl_options.pcmout_driver_name); + + if (cl_options.midiout_driver_name) + { + midiout_driver = old_lookup_driver((old_lookup_funct_t *)midiout_find_driver, + MSVC_FUNCTYPECAST_KLUDGE list_midiout_drivers, + "midiout driver", cl_options.midiout_driver_name); + free(cl_options.midiout_driver_name); + } + + if (cl_options.midi_device_name) + { + midi_device = old_lookup_driver((old_lookup_funct_t *)midi_find_device, + MSVC_FUNCTYPECAST_KLUDGE list_midi_devices, + "MIDI device", cl_options.midi_device_name); + free(cl_options.midi_device_name); + } + + if (cl_options.sound_server_name) + { + sound_server = old_lookup_driver((old_lookup_funct_t *)sound_server_find_driver, + MSVC_FUNCTYPECAST_KLUDGE list_sound_servers, + "sound server", cl_options.sound_server_name); + free(cl_options.sound_server_name); + } +#endif + + if (confs) { + memcpy(gfx_options, &(active_conf->gfx_options), sizeof(gfx_options_t)); /* memcpy so that console works */ + if (!gfx_driver_name) + gfx_driver_name = active_conf->gfx_driver_name; +#ifdef __GNUC__ +#warning "sound" +#endif +#if 0 + if (!sound_server) + sound_server = active_conf->sound_server; + + /* make sure we have sound drivers */ + if (!midiout_driver) + midiout_driver = active_conf->midiout_driver; + if (!midi_device) + midi_device = active_conf->midi_device; + if (!pcmout_driver) + pcmout_driver = active_conf->pcmout_driver; +#endif + } + + if (confs) { + module_path = active_conf->module_path; + + if (!gfx_driver_name) + gfx_driver_name = active_conf->gfx_driver_name; + } + + gfx_driver = (gfx_driver_t *) + lookup_driver((lookup_funct_t *)gfx_find_driver, + MSVC_FUNCTYPECAST_KLUDGE list_graphics_drivers, + "graphics driver", gfx_driver_name, module_path); + + if (!gfx_driver) { + if (gfx_driver_name) + fprintf(stderr,"Failed to find graphics driver \"%s\"\n" + "Please run 'freesci -v' to get a list of all " + "available drivers.\n", gfx_driver_name); + else + fprintf(stderr,"No default gfx driver available.\n"); + + return 1; + } + + if (!gamestate->version_lock_flag) + if (active_conf->version) + gamestate->version = active_conf->version; + + if (strlen (active_conf->debug_mode)) + set_debug_mode (gamestate, 1, active_conf->debug_mode); + + +#if 0 + { + int j; + for (j =0; j < conf_entries; j++) { + int i; + config_entry_t *c = conf + j; + fprintf(stderr, "[%s]\n", c->name); + for (i = 0; i < 2; i++) { + subsystem_options_t *subsys = c->driver_options[i]; + fprintf(stderr, " <%s>\n", i? "midiout" : "gfx"); + + while (subsys) { + driver_option_t *opt; + fprintf(stderr, " {%p,%s}\n", subsys->name,subsys->name); + opt = subsys->options; + while (opt) { + fprintf(stderr, "\t%p'%s' = %p'%s'\n", opt->option, opt->option, opt->value,opt->value); + opt = opt->next; + } + subsys = subsys->next; + } + } + } + } +#endif /* 0 */ + + /* Now configure the graphics driver with the specified options */ + { + driver_option_t *option = get_driver_options(active_conf, FREESCI_DRIVER_SUBSYSTEM_GFX, gfx_driver->name); + while (option) { + if ((gfx_driver->set_parameter)(gfx_driver, option->option, option->value)) { + fprintf(stderr, "Fatal error occured in graphics driver while processing \"%s = %s\"\n", + option->option, option->value); + exit(1); + } + + option = option->next; + } + } + +#ifdef __GNUC__ +#warning "sound" +#endif +#if 0 + /* Configure the pcmout driver */ + { + pcmout_sample_rate = active_conf->pcmout_rate; + pcmout_stereo = active_conf->pcmout_stereo; + } + + /* Configure the midiout driver */ + { + driver_option_t *option = get_driver_options(active_conf, FREESCI_DRIVER_SUBSYSTEM_MIDIOUT, midiout_driver->name); + while (option) { + if ((midiout_driver->set_parameter)(midiout_driver, option->option, option->value)) { + fprintf(stderr, "Fatal error occured in midiout driver while processing \"%s = %s\"\n", + option->option, option->value); + exit(1); + } + + option = option->next; + } + } +#endif + + /* Allows drivers to access files in the resource directory. */ + if (chdir(gamestate->resource_dir)) { + fprintf(stderr,"Error entering resource directory '%s'\n", + gamestate->resource_dir); + exit(1); + } + + if (init_gfx(active_conf, &cl_options, gfx_driver, resmgr)) + return 1; + + + if (game_init_graphics(gamestate)) { /* Init interpreter graphics */ + fprintf(stderr,"Game initialization failed: Error in GFX subsystem. Aborting...\n"); + return 1; + } + + if (game_init_sound(gamestate, (cl_options.master_sound == OFF)? SFX_STATE_FLAG_NOSOUND : 0)) { + fprintf(stderr,"Game initialization failed: Error in sound subsystem. Aborting...\n"); + return 1; + } + + if (chdir(gamestate->work_dir)) { + fprintf(stderr,"Error entering working directory '%s'\n", + gamestate->work_dir); + exit(1); + } + +#ifdef __GNUC__ +#warning "sound" +#endif +#if 0 + if (!sound_server) + sound_server = sound_server_find_driver(NULL); +#endif + + if (cl_options.show_rooms) + set_debug_mode(gamestate, 1, "r"); + +#ifdef __GNUC__ +#warning "sound" +#endif +#if 0 + gamestate->sound_server = sound_server; + + if (gamestate->sound_server) { + int poly; + if (gamestate->sound_server->init( + gamestate, + ((active_conf->reverse_stereo) ? SOUNDSERVER_INIT_FLAG_REVERSE_STEREO : 0))) + { + + fprintf(stderr,"Sound server initialization failed- aborting.\n"); + return 1; + } + sci_sched_yield(); + + if (!soundserver_dead) { + poly = gamestate->sound_server->command(gamestate, get_msg_value("SOUND_COMMAND_TEST"), 0, 0); + + printf("Sound server reports polyphony %d\n", poly); + + gamestate->sound_server->command(gamestate, get_msg_value("SOUND_COMMAND_SET_VOLUME"), 0, 0xc); + + } + + gamestate->sound_server->get_event(gamestate); /* Get init message */ + /* FIXME: memory allocated that is not freed */ + } +#endif + + if (active_conf && active_conf->console_log) + open_console_file (active_conf->console_log); + gamestate->animation_delay = active_conf->animation_delay; + gamestate->animation_granularity = active_conf->animation_granularity; + gfx_crossblit_alpha_threshold = active_conf->alpha_threshold; + + printf("Emulating SCI version %d.%03d.%03d\n", + SCI_VERSION_MAJOR(gamestate->version), + SCI_VERSION_MINOR(gamestate->version), + SCI_VERSION_PATCHLEVEL(gamestate->version)); + + printf("Graphics: Using the %s driver %s\n", + gfx_driver->name, gfx_driver->version); +#ifdef __GNUC__ +#warning "sound" +#endif +#if 0 + printf("MIDI-out: Using the %s driver %s\n", + midiout_driver->name, midiout_driver->version); + printf("MIDI-device: Using the %s driver %s\n", + midi_device->name, midi_device->version); + printf("PCM-out: Using the %s driver %s\n", + pcmout_driver->name, pcmout_driver->version); + + if (sound_server) + printf("Sound server: Using the %s sound server %s\n", + sound_server->name, sound_server->version); + else + printf("Sound server: Disabled.\n"); +#endif + + gamestate->have_mouse_flag = (cl_options.mouse == DONTCARE)? + active_conf->mouse : cl_options.mouse; + + if (savegame_name) + game_restore(&gamestate, savegame_name); + else + game_run(&gamestate); /* Run the game */ +#ifdef __GNUC__ +#warning "sound" +#endif +#if 0 + if (gamestate->sound_server) + gamestate->sound_server->exit(gamestate); /* Shutdown sound daemon first */ +#endif + + game_exit(gamestate); + script_free_engine(gamestate); /* Uninitialize game state */ + script_free_breakpoints(gamestate); + + scir_free_resource_manager(resmgr); + + if (conf_entries >= 0) + config_free(&confs, conf_entries); + + close_console_file(); + + chdir (startdir); /* ? */ + +#ifdef HAVE_FORK + printf("Waiting for sound server to die..."); + wait(NULL); /* Wait for sound server process to die, if neccessary */ + printf(" OK.\n"); +#endif + + gfxop_exit(gfx_state); + + sci_free(gamestate); + + if (commandline_config_file) + sci_free(commandline_config_file); + +#ifdef WITH_DMALLOC + fprintf(stderr,"--- Everything but the two console buffers should have been freed now ---\n"); + dmalloc_log_unfreed(); +/* BREAKPOINT(); */ + ((*(int *)NULL) = 42); +#endif + return 0; +} + + +static int +game_select_resource_found() +{ + int fd; + + fd = sci_open("resource.map", O_RDONLY | O_BINARY); + if (IS_VALID_FD(fd)) + { + close(fd); + return 1; + } + + return 0; +} + +static int +game_select_init_gfx(config_entry_t *conf, cl_options_t *cl_options, gfx_driver_t *driver, sci_version_t sci_version) +{ + int scale_x = 0, scale_y = 0; + int color_depth = 0; + + if (conf) { + if (conf->scale) + scale_x = scale_y = conf->scale; + + if (conf->x_scale) + scale_x = conf->x_scale; + + if (conf->y_scale) + scale_y = conf->y_scale; + + if (conf->color_depth) + color_depth = conf->color_depth >> 3; /* In there it's bpp */ + } + + gfx_state->driver = driver; + gfx_state->version = sci_version; + + if (cl_options->scale_x > 0) { + scale_x = cl_options->scale_x; + + if (!scale_y) + scale_y = cl_options->scale_x; + } + + if (cl_options->scale_y > 0) { + scale_y = cl_options->scale_y; + + if (!scale_x) + scale_x = cl_options->scale_y; + } + + if (cl_options->color_depth > 0) + color_depth = cl_options->color_depth; + + if (color_depth > 0 && scale_x == 0) + scale_x = scale_y = 2; /* Some default setting */ + +/* fprintf(stderr, "cd-conf=%d, cd-cl=%d, cd=%d\n", + conf->color_depth, cl_options->color_depth, color_depth); */ + + fprintf(stderr, "Checking byte depth %d\n", color_depth); + + if (scale_x > 0) { + + if (color_depth > 0) { + if (game_select_gfxop_init(gfx_state, scale_x, + scale_y, (gfx_color_mode_t) color_depth, + gfx_options, 0)) { + fprintf(stderr,"Graphics initialization failed. Aborting...\n"); + return 1; + } + } else { + color_depth = 4; + while (game_select_gfxop_init(gfx_state, scale_x, + scale_y, (gfx_color_mode_t) color_depth, + gfx_options, 0) && --color_depth); + + if (!color_depth) { + fprintf(stderr,"Could not find a matching color depth. Aborting...\n"); + return 1; + } + } + + } else if (game_select_gfxop_init_default(gfx_state, gfx_options, 0)) { + fprintf(stderr,"Graphics initialization failed. Aborting...\n"); + return 1; + } + + return 0; +} + +static int compare_games(const void* arg1, const void* arg2) +{ + game_t* game1 = (game_t *)arg1; + game_t* game2 = (game_t *)arg2; + + return strcmp(game1->name, game2->name); +} + +static gfx_bitmap_font_t* load_font(const char* font_dir, const char* filename) +{ + gfx_bitmap_font_t* font = NULL; + int fh; + int fsize; + byte* buffer; + char work_dir[256]; + + getcwd(work_dir, 256); + + /* change to the font directory */ + chdir(font_dir); + + fh = sci_open(filename, O_RDONLY|O_BINARY); + + if (!IS_VALID_FD(fh)) + return NULL; + + fsize = sci_fd_size(fh); + + buffer = (byte *) sci_malloc(fsize); + + /* skip the header information, is present by default when using sciunpack */ + read(fh, buffer, 2); + + /* read the font data */ + read(fh, buffer, fsize); + + font = gfxr_read_font(0, buffer, fsize); + + sci_free(buffer); + + close(fh); + + chdir(work_dir); + + return font; +} + +#define FONT_DEFAULT "default.fnt" +#define FONT_SMALL "small.fnt" + +static void +add_games(const char *dir_name, games_list_head_t *head, int *games, gfx_driver_t *driver, + gfx_bitmap_font_t* font_default, gfx_bitmap_font_t* font_small) +{ + sci_dir_t dir; + char *filename; + int fd; + + if (chdir(dir_name)) + return; + + fd = sci_open("resource.map", O_RDONLY); + + if (IS_VALID_FD(fd)) { + games_list_t *list; + sci_version_t result; + int res_version; + const char *game_name; + guint32 code; + + close(fd); + + list = (games_list_t*)sci_malloc(sizeof(games_list_t)); + getcwd(list->game.dir, PATH_MAX); + game_name = version_guess_from_hashcode(&result, &res_version, &code); + + if (game_name) + list->game.name = sci_strdup(game_name); + else + list->game.name = sci_strdup(dir_name); + + list->game.conf_nr = 0; + LIST_INSERT_HEAD(head, list, entries); + (*games)++; + + game_select_scan_info(driver, font_default, font_small, list->game.name, *games); + } + + sci_init_dir(&dir); + + filename = sci_find_first(&dir, "*"); + + while (filename) { + add_games(filename, head, games, driver, font_default, font_small); + filename = sci_find_next(&dir); + } + + sci_finish_find(&dir); + chdir(".."); + + return; +} + +static int +find_games(const char* dir, game_t **games, gfx_driver_t *driver, gfx_bitmap_font_t* font_default, gfx_bitmap_font_t* font_small) +{ + games_list_head_t head; + int games_nr = 0; + games_list_t *list; + int i; + + game_select_scan_info(driver, font_default, font_small, NULL, 0); + + LIST_INIT(&head); + add_games(dir, &head, &games_nr, driver, font_default, font_small); + + if (!games_nr) + return 0; + + *games = (game_t*)sci_malloc(sizeof(game_t) * games_nr); + + i = 0; + while ((list = LIST_FIRST(&head))) { + (*games)[i++] = list->game; + LIST_REMOVE(list, entries); + sci_free(list); + } + + return games_nr; +} + +static int game_select(cl_options_t cl_options, config_entry_t *confs, int conf_entries, const char* freesci_dir, const char *games_dir) { + char start_dir[PATH_MAX+1] = ""; + char *gfx_driver_name = NULL; + gfx_driver_t *gfx_driver = NULL; + const char *module_path = SCI_DEFAULT_MODULE_PATH; + int selected_game = -1; + gfx_bitmap_font_t* font_default; + gfx_bitmap_font_t* font_small; + int font_default_allocated = 0; + int font_small_allocated = 0; + game_t *games = NULL; + int games_nr; + int i; + + getcwd(start_dir, PATH_MAX); + script_debug_flag = cl_options.script_debug_flag; + + /* gcc doesn't warn about (void *)s being typecast. If your compiler doesn't like these + ** implicit casts, don't hesitate to typecast appropriately. */ + if (cl_options.gfx_driver_name) { + gfx_driver_name = sci_strdup(cl_options.gfx_driver_name); + /* free(cl_options.gfx_driver_name); */ + } /* else it's still NULL */ + + if (confs) { + memcpy(gfx_options, &(confs->gfx_options), sizeof(gfx_options_t)); /* memcpy so that console works */ + if (!gfx_driver_name) + gfx_driver_name = confs->gfx_driver_name; + } + + if (confs) { + module_path = confs->module_path; + + if (!gfx_driver_name) + gfx_driver_name = confs->gfx_driver_name; + } + + gfx_driver = (gfx_driver_t *) + lookup_driver((lookup_funct_t *)gfx_find_driver, + MSVC_FUNCTYPECAST_KLUDGE list_graphics_drivers, + "graphics driver", gfx_driver_name, module_path); + + if (!gfx_driver) { + if (gfx_driver_name) + fprintf(stderr,"Failed to find graphics driver \"%s\"\n" + "Please run 'freesci -v' to get a list of all " + "available drivers.\n", gfx_driver_name); + else + fprintf(stderr,"No default gfx driver available.\n"); + + return -2; + } + + + /* Now configure the graphics driver with the specified options */ + { + driver_option_t *option = get_driver_options(confs, FREESCI_DRIVER_SUBSYSTEM_GFX, gfx_driver->name); + while (option) { + if ((gfx_driver->set_parameter)(gfx_driver, option->option, option->value)) { + fprintf(stderr, "Fatal error occured in graphics driver while processing \"%s = %s\"\n", + option->option, option->value); + exit(1); + } + + option = option->next; + } + } + + if (game_select_init_gfx(confs, &cl_options, gfx_driver, 0)) + return -2; + + /* load user supplied font from disk, if not found then use built-in font */ + font_default = load_font(freesci_dir, FONT_DEFAULT); + if (!font_default) + font_default = gfxr_get_font(NULL, GFX_FONT_BUILTIN_6x10, 0); + else + font_default_allocated = 1; + + /* load user supplied font from disk, if not found then use built-in font */ + font_small = load_font(freesci_dir, FONT_SMALL); + if (!font_small) + font_small = gfxr_get_font(NULL, GFX_FONT_BUILTIN_5x8, 0); + else + font_small_allocated = 1; + + chdir(start_dir); + + if (games_dir) + games_nr = find_games(games_dir, &games, gfx_driver, font_default, font_small); + else + games_nr = 0; + + games = (game_t*)sci_realloc(games, sizeof(game_t) * (games_nr + conf_entries)); + + for (i = 0; i < conf_entries; i++) { + if (confs[i].name) { + char *c; + + games[games_nr].name = sci_strdup(confs[i].name); + games[games_nr].conf_nr = i; + /* Replace all '_'with ' ' */ + + c = games[games_nr].name; + while (*c != 0) + { + if (*c == '_') + *c = ' '; + c++; + } + strncpy(games[games_nr].dir, confs[i].resource_dir, PATH_MAX - 1); + games[games_nr++].dir[PATH_MAX - 1] = 0; + } + } + + /* Sort game list */ + qsort(games, games_nr, sizeof(game_t), compare_games); + + /* Index of game selected is returned - -1 means no selection (quit) */ + selected_game = game_select_display(gfx_driver, games, games_nr, font_default, font_small); + if (selected_game >= 0) + { + chdir(games[selected_game].dir); + } + + if (font_default_allocated == 1) + gfxr_free_font(font_default); + + if (font_small_allocated == 1) + gfxr_free_font(font_small); + + if (selected_game >= 0) + selected_game = games[selected_game].conf_nr; + + for (i = 0; i < games_nr; i++) + sci_free(games[i].name); + sci_free(games); + + gfx_driver->exit(gfx_driver); + + return selected_game; +} diff --git a/engines/sci/makefile.dos b/engines/sci/makefile.dos new file mode 100644 index 0000000000..6bfbb3fcf9 --- /dev/null +++ b/engines/sci/makefile.dos @@ -0,0 +1,20 @@ +# +# FreeSCI/DOS Makefile +# +# 19991220 rink created this file +# +# +TARGET : freesci.exe + +FILES = main.o config.o +LIBS = engine/libsciengine.a graphics/scigraphics.a scicore/scicore.a \ + sound/scisound.a + +CC = gcc +CFLAGS = -g -c -Iinclude -I../.. -D_DOS -DHAVE_LIBPNG -DHAVE_DGFX + +clean: + del *.o *.a + +freesci.exe: ${FILES} ${LIBS} + gcc -g -o freesci.exe ${FILES} ${LIBS} -lpng -lz diff --git a/engines/sci/menu/Makefile.am b/engines/sci/menu/Makefile.am new file mode 100644 index 0000000000..6c8f0b53de --- /dev/null +++ b/engines/sci/menu/Makefile.am @@ -0,0 +1,5 @@ +INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@ +AM_CFLAGS = $(SDL_CFLAGS) +LDADD = -lc +noinst_LIBRARIES = libscimenu.a +libscimenu_a_SOURCES = game_select_init.c game_select_screen.c diff --git a/engines/sci/menu/game_select_init.c b/engines/sci/menu/game_select_init.c new file mode 100644 index 0000000000..b538a60b38 --- /dev/null +++ b/engines/sci/menu/game_select_init.c @@ -0,0 +1,275 @@ +/*************************************************************************** + game_select_init.c Copyright (C) 2004 Hugues Valois + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include +#include "game_select.h" +#include "sciresource.h" + +/* NOTE: THIS CODE BASED ON _gfxop_init_common (COPIED AND MODIFIED) */ +static int +game_select_gfxop_init_common(gfx_state_t *state, gfx_options_t *options, void *misc_payload) +{ + int i; + + state->options = options; + + if ((state->static_palette = gfxr_interpreter_get_static_palette(state->resstate, + SCI_VERSION_0, + &(state->static_palette_entries), + misc_payload))) + { + for (i = 0; i < state->static_palette_entries; i++) + gfx_alloc_color(state->driver->mode->palette, state->static_palette + i); + } + +/* if (!((state->resstate = gfxr_new_resource_manager(state->version, */ +/* state->options, */ +/* state->driver, */ +/* misc_payload)))) { */ +/* GFXERROR("Failed to initialize resource manager!\n"); */ +/* return GFX_FATAL; */ +/* } */ + + + state->visible_map = GFX_MASK_VISUAL; + gfxop_set_clip_zone(state, gfx_rect(0, 0, 320, 200)); + + state->mouse_pointer = state->mouse_pointer_bg = NULL; + state->mouse_pointer_visible = 0; + state->control_map = gfx_pixmap_alloc_index_data(gfx_new_pixmap(320, 200, GFX_RESID_NONE, 0, 0)); + state->control_map->flags |= GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + state->options = options; + state->mouse_pointer_in_hw = 0; + state->disable_dirty = 0; + state->events = NULL; + + state->pic = state->pic_unscaled = NULL; + + state->pic_nr = -1; /* Set background pic number to an invalid value */ + + state->tag_mode = 0; + + state->dirty_rects = NULL; + + + return GFX_OK; +} + +/* NOTE: THIS CODE BASED ON gfxop_init_default (COPIED AND MODIFIED) */ +int +game_select_gfxop_init_default(gfx_state_t *state, gfx_options_t *options, void *misc_info) +{ + if (state->driver->init(state->driver)) + return GFX_FATAL; + + return game_select_gfxop_init_common(state, options, misc_info); +} + +/* NOTE: THIS CODE BASED ON gfxop_init (COPIED AND MODIFIED) */ +int +game_select_gfxop_init(gfx_state_t *state, int xfact, int yfact, gfx_color_mode_t bpp, + gfx_options_t *options, void *misc_info) +{ + int color_depth = bpp? bpp : 1; + int initialized = 0; + + do { + if (!state->driver->init_specific(state->driver, xfact, yfact, color_depth)) + initialized = 1; + else + color_depth++; + } while (!initialized && color_depth < 9 && !bpp); + + if (!initialized) + return GFX_FATAL; + + return game_select_gfxop_init_common(state, options, misc_info); +} + +#if 0 +/* this can be used to generate code that creates a particular font at runtime */ +/* this is meant to be used as a development tool */ +void save_font(int id, gfx_bitmap_font_t* font) +{ + FILE* file; + char filepath[128]; + char filename[128]; + char buffer[1024]; + int i = 0; + + _itoa(id, filename, 10); + strcpy(filepath, filename); + strcat(filepath, ".c"); + + file = fopen(filepath, "w"); + + strcpy(buffer, "#include \n#include \n#include \n#include \n\n"); + fwrite(buffer, strlen(buffer), 1, file); + + /* font widths */ + strcpy(buffer, "static int font_widths[] = \n{\n"); + fwrite(buffer, strlen(buffer), 1, file); + + for (i = 0; i < font->chars_nr; i++) + { + strcpy(buffer, "\t"); + fwrite(buffer, strlen(buffer), 1, file); + + _itoa(font->widths[i], buffer, 10); + fwrite(buffer, strlen(buffer), 1, file); + + strcpy(buffer, ",\n"); + fwrite(buffer, strlen(buffer), 1, file); + } + + strcpy(buffer, "};\n\n"); + fwrite(buffer, strlen(buffer), 1, file); + + /* font data */ + strcpy(buffer, "static byte font_data[] = \n{\n"); + fwrite(buffer, strlen(buffer), 1, file); + + for (i = 0; i < font->chars_nr * font->height * font->row_size; i++) + { + strcpy(buffer, "\t"); + fwrite(buffer, strlen(buffer), 1, file); + + _itoa(font->data[i], buffer, 10); + fwrite(buffer, strlen(buffer), 1, file); + + strcpy(buffer, ",\n"); + fwrite(buffer, strlen(buffer), 1, file); + } + + strcpy(buffer, "};\n\n"); + fwrite(buffer, strlen(buffer), 1, file); + + /* font structure */ + strcpy(buffer, "static gfx_bitmap_font_t font = \n{\n"); + fwrite(buffer, strlen(buffer), 1, file); + + /* ID */ + strcpy(buffer, "\t"); + fwrite(buffer, strlen(buffer), 1, file); + + _itoa(id, buffer, 10); + fwrite(buffer, strlen(buffer), 1, file); + + strcpy(buffer, ",\n"); + fwrite(buffer, strlen(buffer), 1, file); + + /* chars_nr */ + strcpy(buffer, "\t"); + fwrite(buffer, strlen(buffer), 1, file); + + _itoa(font->chars_nr, buffer, 10); + fwrite(buffer, strlen(buffer), 1, file); + + strcpy(buffer, ",\n"); + fwrite(buffer, strlen(buffer), 1, file); + + /* widths */ + strcpy(buffer, "\t"); + fwrite(buffer, strlen(buffer), 1, file); + + strcpy(buffer, "font_widths,\n"); + fwrite(buffer, strlen(buffer), 1, file); + + /* row_size */ + strcpy(buffer, "\t"); + fwrite(buffer, strlen(buffer), 1, file); + + _itoa(font->row_size, buffer, 10); + fwrite(buffer, strlen(buffer), 1, file); + + strcpy(buffer, ",\n"); + fwrite(buffer, strlen(buffer), 1, file); + + /* line_height */ + strcpy(buffer, "\t"); + fwrite(buffer, strlen(buffer), 1, file); + + _itoa(font->line_height, buffer, 10); + fwrite(buffer, strlen(buffer), 1, file); + + strcpy(buffer, ",\n"); + fwrite(buffer, strlen(buffer), 1, file); + + /* height */ + strcpy(buffer, "\t"); + fwrite(buffer, strlen(buffer), 1, file); + + _itoa(font->height, buffer, 10); + fwrite(buffer, strlen(buffer), 1, file); + + strcpy(buffer, ",\n"); + fwrite(buffer, strlen(buffer), 1, file); + + /* char_size */ + strcpy(buffer, "\t"); + fwrite(buffer, strlen(buffer), 1, file); + + _itoa(font->char_size, buffer, 10); + fwrite(buffer, strlen(buffer), 1, file); + + strcpy(buffer, ",\n"); + fwrite(buffer, strlen(buffer), 1, file); + + /* data */ + strcpy(buffer, "\t"); + fwrite(buffer, strlen(buffer), 1, file); + + strcpy(buffer, "font_data,\n"); + fwrite(buffer, strlen(buffer), 1, file); + + strcpy(buffer, "};\n\n"); + fwrite(buffer, strlen(buffer), 1, file); + + /* function definition */ + strcpy(buffer, "gfx_bitmap_font_t* get_font_"); + fwrite(buffer, strlen(buffer), 1, file); + + strcpy(buffer, filename); + fwrite(buffer, strlen(buffer), 1, file); + + /* function body start*/ + strcpy(buffer, "()\n{\n"); + fwrite(buffer, strlen(buffer), 1, file); + + /* return */ + strcpy(buffer, "\treturn &font;\n"); + fwrite(buffer, strlen(buffer), 1, file); + + /* function body end */ + strcpy(buffer, "}\n"); + fwrite(buffer, strlen(buffer), 1, file); + + fclose(file); +} +#endif + diff --git a/engines/sci/menu/game_select_screen.c b/engines/sci/menu/game_select_screen.c new file mode 100644 index 0000000000..fe27d597d9 --- /dev/null +++ b/engines/sci/menu/game_select_screen.c @@ -0,0 +1,581 @@ +/*************************************************************************** + game_select_screen.c Copyright (C) 2004 Hugues Valois + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +#include +#include +#include "game_select.h" + + +#define MILLION 1000000 + + +#define GS_WINDOW_LEFT 30 +#define GS_WINDOW_TOP 26 +#define GS_WINDOW_WIDTH 260 +#define GS_WINDOW_HEIGHT 156 + +#define GS_SCAN_WINDOW_LEFT 30 +#define GS_SCAN_WINDOW_TOP 73 +#define GS_SCAN_WINDOW_WIDTH 260 +#define GS_SCAN_WINDOW_HEIGHT 62 + +#define GS_LISTBOX_WIDTH (GS_WINDOW_WIDTH - 78) +#define GS_LISTBOX_HEIGHT (GS_WINDOW_HEIGHT - 43) +#define GS_LISTBOX_CONTENTS_HEIGHT GS_LISTBOX_HEIGHT - 21 + +#define GS_BUTTON_PLAY 1 +#define GS_BUTTON_QUIT 2 + +#define GS_BUTTON_FIRST 1 +#define GS_BUTTON_LAST 2 + +static int +game_select_gfxop_usleep(gfx_driver_t *gfx_driver, long usecs) +{ + long time, utime; + long wakeup_time, wakeup_utime; + long add_seconds; + int retval = GFX_OK; + + sci_gettime(&wakeup_time, &wakeup_utime); + wakeup_utime += usecs; + + add_seconds = (wakeup_utime / MILLION); + wakeup_time += add_seconds; + wakeup_utime -= (MILLION * add_seconds); + + do { + sci_gettime(&time, &utime); + usecs = (wakeup_time - time) * MILLION + wakeup_utime - utime; + } while ((usecs > 0) && !(retval = gfx_driver->usec_sleep(gfx_driver, usecs))); + + if (retval) { + GFXWARN("Waiting failed\n"); + } + + return retval; +} + +static sci_event_t +game_select_gfxop_get_event(gfx_driver_t *gfx_driver, unsigned int mask) +{ + sci_event_t event; + + event.type = 0; + + do + { + event = gfx_driver->get_event(gfx_driver); + + } while (event.type && !(event.type & mask)); + + return event; +} + +static gfx_pixmap_color_t +create_pixmap_color_t(gfx_color_t color) +{ + gfx_pixmap_color_t pixmap_color; + + pixmap_color.global_index = GFX_COLOR_INDEX_UNMAPPED; + pixmap_color.r = color.visual.r; + pixmap_color.g = color.visual.g; + pixmap_color.b = color.visual.b; + + return pixmap_color; +} + +static gfx_color_t +create_color_t(byte r, byte g, byte b) +{ + gfx_color_t color; + + color.visual.global_index = 0; + color.mask = GFX_MASK_VISUAL; + color.alpha = 1; + color.priority = 0; + color.control = 0; + color.visual.r = r; + color.visual.g = g; + color.visual.b = b; + + return color; +} + +static void +gfx_box_border(gfx_driver_t *gfx_driver, rect_t box, gfx_color_t color) +{ + box.x *= gfx_driver->mode->xfact; + box.y *= gfx_driver->mode->yfact; + box.xl *= gfx_driver->mode->xfact; + box.yl *= gfx_driver->mode->yfact; + + { + point_t ul = gfx_point (box.x, box.y); + point_t ur = gfx_point (box.x + box.xl, box.y); + point_t ll = gfx_point (box.x, box.y + box.yl); + point_t lr = gfx_point (box.x + box.xl, box.y + box.yl); + + + gfx_driver->draw_line(gfx_driver, ul, ur, color, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL); + gfx_driver->draw_line(gfx_driver, ur, lr, color, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL); + gfx_driver->draw_line(gfx_driver, lr, ll, color, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL); + gfx_driver->draw_line(gfx_driver, ll, ul, color, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL); + } +} + +static void +gfx_box_fill(gfx_driver_t *gfx_driver, rect_t box, gfx_color_t color) +{ + box.x *= gfx_driver->mode->xfact; + box.y *= gfx_driver->mode->yfact; + box.xl *= gfx_driver->mode->xfact; + box.yl *= gfx_driver->mode->yfact; + + gfx_driver->draw_filled_rect(gfx_driver, box, color, color, GFX_SHADE_FLAT); +} + +static void +gfx_box_line(gfx_driver_t *gfx_driver, rect_t box, gfx_color_t color) +{ + box.x *= gfx_driver->mode->xfact; + box.y *= gfx_driver->mode->yfact; + box.xl *= gfx_driver->mode->xfact; + box.yl *= gfx_driver->mode->yfact; + + + { + point_t p1 = gfx_point (box.x, box.y); + point_t p2 = gfx_point (box.x + box.xl, box.y + box.yl); + + gfx_driver->draw_line(gfx_driver, p1, p2, color, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL); + } +} + +static void +gfx_box_text(gfx_driver_t *gfx_driver, rect_t box, gfx_bitmap_font_t* font, gfx_pixmap_color_t fgc, gfx_pixmap_color_t bgc, const char* text, int center) +{ + int width; + int height; + gfx_pixmap_t* pixmap; + text_fragment_t *textsplits; + int meas_width; + int meas_height; + int meas_line; + int margin_x = 0; + int margin_y = 0; + int line_height; + int last_offset; + + if (center != 0) + { + textsplits = gfxr_font_calculate_size(font, box.xl, text, &meas_width, &meas_height, &meas_line, &line_height, &last_offset, GFXR_FONT_FLAG_NO_NEWLINES | GFXR_FONT_FLAG_COUNT_WHITESPACE); + if (textsplits != NULL) + { + free(textsplits); + } + + margin_x = (box.xl - meas_width) / 2; + margin_y = (box.yl - meas_height) / 2; + } + + box.x *= gfx_driver->mode->xfact; + box.y *= gfx_driver->mode->yfact; + box.xl *= gfx_driver->mode->xfact; + box.yl *= gfx_driver->mode->yfact; + margin_x *= gfx_driver->mode->xfact; + margin_y *= gfx_driver->mode->yfact; + + pixmap = gfxr_draw_font(font, text, strlen(text), &fgc, &fgc, NULL); + if (pixmap != NULL) + { + pixmap->xoffset = 0; + pixmap->yoffset = 0; + + gfx_xlate_pixmap(gfx_pixmap_alloc_data(pixmap, gfx_driver->mode), gfx_driver->mode, GFX_XLATE_FILTER_NONE); + + width = pixmap->index_xl * gfx_driver->mode->xfact; + height = pixmap->index_yl * gfx_driver->mode->yfact; + + if (width > box.xl) + width = box.xl; + + if (gfx_driver->draw_pixmap(gfx_driver, pixmap, GFX_NO_PRIORITY, gfx_rect(0, 0, width, height), gfx_rect(box.x + margin_x, box.y + margin_y, width, height), GFX_BUFFER_BACK) != GFX_OK) + { + GFXERROR("Error occured while drawing pixmap.\n"); + } + + gfx_free_pixmap(gfx_driver, pixmap); + } +} + +static void +game_select_display_button(gfx_driver_t *gfx_driver, gfx_bitmap_font_t* font, rect_t box, const char* button_text, int focus) +{ + gfx_color_t black; + gfx_color_t white; + + black = create_color_t(0, 0, 0); + white = create_color_t(255, 255, 255); + + gfx_box_border(gfx_driver, gfx_rect(box.x, box.y, box.xl, box.yl), black); + gfx_box_fill(gfx_driver, gfx_rect(box.x + 1, box.y + 1, box.xl - 2, box.yl - 2), white); + gfx_box_text(gfx_driver, gfx_rect(box.x, box.y + 1, box.xl, box.yl), font, create_pixmap_color_t(black), create_pixmap_color_t(white), button_text, 1); + if (focus != 0) + gfx_box_border(gfx_driver, gfx_rect(box.x + 1, box.y + 1, box.xl - 2, box.yl - 2), black); +} + +static void +game_select_display_listbox(gfx_driver_t *gfx_driver, gfx_bitmap_font_t* font, rect_t box, game_t *game_list, int game_count, int first_game, int selected_game) +{ + int max_game; + int pos = 0; + int cur; + int center_x; + gfx_color_t black; + gfx_color_t white; + + center_x = box.x + (box.xl / 2) - 1; + + black = create_color_t(0, 0, 0); + white = create_color_t(255, 255, 255); + + /* list box */ + gfx_box_border(gfx_driver, box, black); + gfx_box_fill(gfx_driver, gfx_rect(box.x + 1, box.y + 1, box.xl - 2, box.yl - 2), white); + gfx_box_line(gfx_driver, gfx_rect(box.x, box.y + 10, box.xl, 0), black); + gfx_box_line(gfx_driver, gfx_rect(box.x, box.y + box.yl - 10, box.xl, 0), black); + + /* list box scroll up */ + gfx_box_line(gfx_driver, gfx_rect(center_x, box.y + 2, 1, 0), black); + gfx_box_line(gfx_driver, gfx_rect(center_x - 1, box.y + 3, 3, 0), black); + gfx_box_line(gfx_driver, gfx_rect(center_x - 2, box.y + 4, 5, 0), black); + gfx_box_line(gfx_driver, gfx_rect(center_x - 3, box.y + 5, 7, 0), black); + gfx_box_fill(gfx_driver, gfx_rect(center_x - 1, box.y + 6, 4, 3), black); + + /* list box scroll up */ + gfx_box_line(gfx_driver, gfx_rect(center_x, box.y + box.yl - 2, 1, 0), black); + gfx_box_line(gfx_driver, gfx_rect(center_x - 1, box.y + box.yl - 3, 3, 0), black); + gfx_box_line(gfx_driver, gfx_rect(center_x - 2, box.y + box.yl - 4, 5, 0), black); + gfx_box_line(gfx_driver, gfx_rect(center_x - 3, box.y + box.yl - 5, 7, 0), black); + gfx_box_fill(gfx_driver, gfx_rect(center_x - 1, box.y + box.yl - 8, 4, 3), black); + + /* list box content */ + max_game = (int)(((double)(box.yl - 21)) / font->line_height); + for (cur = first_game; cur < game_count && (cur - first_game < max_game); cur++, pos++) + { + if (selected_game == cur) + gfx_box_fill(gfx_driver, gfx_rect(box.x + 1, box.y + 11 + (pos * font->line_height), box.xl - 1, font->line_height), black); + + gfx_box_text(gfx_driver, gfx_rect(box.x + 1, box.y + 11 + (pos * font->line_height), box.xl - 1, font->line_height), font, create_pixmap_color_t((selected_game == cur) ? white : black), create_pixmap_color_t((selected_game == cur) ? black : white), game_list[cur].name, 0); + } +} + +static void +game_select_display_window(gfx_driver_t *gfx_driver, gfx_bitmap_font_t* font, rect_t box, const char* title) +{ + gfx_color_t black; + gfx_color_t white; + gfx_color_t grey; + + black = create_color_t(0, 0, 0); + white = create_color_t(255, 255, 255); + grey = create_color_t(85, 85, 85); + + /* window border */ + gfx_box_border(gfx_driver, box, black); + + /* window fill */ + gfx_box_fill(gfx_driver, gfx_rect(box.x + 1, box.y + 1, box.xl - 1, box.yl - 1), white); + + /* window title */ + gfx_box_fill(gfx_driver, gfx_rect(box.x + 1, box.y + 1, box.xl - 1, font->height), grey); + gfx_box_line(gfx_driver, gfx_rect(box.x, box.y + font->height + 1, box.xl, 0), black); + gfx_box_text(gfx_driver, gfx_rect(box.x + 1, box.y + 2, box.xl - 1, font->height - 1), font, create_pixmap_color_t(white), create_pixmap_color_t(grey), title, 1); + + /* window shade */ + gfx_box_line(gfx_driver, gfx_rect(box.x + box.xl + 1, box.y + 3, 0, box.yl - 2), black); + gfx_box_line(gfx_driver, gfx_rect(box.x + 3, box.y + box.yl + 1, box.xl - 2, 0), black); +} + +static void +game_select_draw_bg(gfx_driver_t *gfx_driver, gfx_bitmap_font_t* font) +{ + char title[255]; + gfx_color_t black; + gfx_color_t white; + gfx_color_t blue; + + black = create_color_t(0, 0, 0); + white = create_color_t(255, 255, 255); + blue = create_color_t(0, 0, 170); + + strcpy(title, "FreeSCI"); + strcat(title, " "); + strcat(title, VERSION); + + /* menu bar */ + gfx_box_fill(gfx_driver, gfx_rect(0, 0, 320, font->height), white); + gfx_box_line(gfx_driver, gfx_rect(0, font->height, 320, 0), black); + gfx_box_text(gfx_driver, gfx_rect(0, 1, 320, font->height - 1), font, create_pixmap_color_t(black), create_pixmap_color_t(white), title, 1); + + /* background */ + gfx_box_fill(gfx_driver, gfx_rect(0, 10, 320, 190), blue); +} + +static void +game_select_display_game_list(gfx_driver_t *gfx_driver, gfx_bitmap_font_t* font_default, gfx_bitmap_font_t* font_small, game_t *game_list, int game_count, int first_game, int selected_game, int focus_button) +{ + gfx_color_t black; + gfx_color_t white; + + black = create_color_t(0, 0, 0); + white = create_color_t(255, 255, 255); + + game_select_draw_bg(gfx_driver, font_default); + + /* window */ + game_select_display_window(gfx_driver, font_default, gfx_rect(GS_WINDOW_LEFT, GS_WINDOW_TOP, GS_WINDOW_WIDTH, GS_WINDOW_HEIGHT), "Play a Game"); + + /* window text */ + gfx_box_text(gfx_driver, gfx_rect(GS_WINDOW_LEFT + 6, GS_WINDOW_TOP + 16, GS_WINDOW_WIDTH - 11, 8), font_default, create_pixmap_color_t(black), create_pixmap_color_t(white), "Select the game that you would like", 0); + gfx_box_text(gfx_driver, gfx_rect(GS_WINDOW_LEFT + 6, GS_WINDOW_TOP + 24, GS_WINDOW_WIDTH - 11, 8), font_default, create_pixmap_color_t(black), create_pixmap_color_t(white), "to play.", 0); + + /* window list box */ + game_select_display_listbox(gfx_driver, font_small, gfx_rect(GS_WINDOW_LEFT + 5, GS_WINDOW_TOP + 39, GS_LISTBOX_WIDTH, GS_LISTBOX_HEIGHT), game_list, game_count, first_game, selected_game); + + /* window play button */ + game_select_display_button(gfx_driver, font_default, gfx_rect(GS_WINDOW_LEFT + GS_WINDOW_WIDTH - 69, GS_WINDOW_TOP + 39, 64, font_default->height + 1), "Play", focus_button == GS_BUTTON_PLAY); + + /* window quit button */ + game_select_display_button(gfx_driver, font_default, gfx_rect(GS_WINDOW_LEFT + GS_WINDOW_WIDTH - 69, GS_WINDOW_TOP + 39 + font_default->height + 5, 64, font_default->height + 1), "Quit", focus_button == GS_BUTTON_QUIT); +} + +void +game_select_scan_info(gfx_driver_t *gfx_driver, gfx_bitmap_font_t* font_default, gfx_bitmap_font_t* font_small, char *name, int total) +{ + gfx_color_t black; + gfx_color_t white; + gfx_color_t grey; + gfx_color_t blue; + rect_t box; + int error; + char text[256]; + + black = create_color_t(0, 0, 0); + white = create_color_t(255, 255, 255); + grey = create_color_t(85, 85, 85); + blue = create_color_t(0, 0, 170); + + game_select_draw_bg(gfx_driver, font_default); + + /* window */ + game_select_display_window(gfx_driver, font_default, gfx_rect(GS_SCAN_WINDOW_LEFT, GS_SCAN_WINDOW_TOP, GS_SCAN_WINDOW_WIDTH, GS_SCAN_WINDOW_HEIGHT), "Scanning for Games"); + + /* window text */ + gfx_box_text(gfx_driver, gfx_rect(GS_SCAN_WINDOW_LEFT + 6, GS_SCAN_WINDOW_TOP + 16, GS_SCAN_WINDOW_WIDTH - 11, 8), font_default, create_pixmap_color_t(black), create_pixmap_color_t(white), "Please wait, scanning for games...", 0); + + if (name) { + snprintf(text, 256, "Adding: %s", name); + + gfx_box_text(gfx_driver, gfx_rect(GS_SCAN_WINDOW_LEFT + 6, GS_SCAN_WINDOW_TOP + 32, GS_SCAN_WINDOW_WIDTH - 11, 8), font_default, create_pixmap_color_t(black), create_pixmap_color_t(white), text, 0); + } + + snprintf(text, 256, "Games found: %i", total); + + gfx_box_text(gfx_driver, gfx_rect(GS_SCAN_WINDOW_LEFT + 6, GS_SCAN_WINDOW_TOP + 48, GS_SCAN_WINDOW_WIDTH - 11, 8), font_default, create_pixmap_color_t(black), create_pixmap_color_t(white), text, 0); + + box = gfx_rect(0, 0, 320 * gfx_driver->mode->xfact, 200 * gfx_driver->mode->yfact); + + if ((error = gfx_driver->update(gfx_driver, box, gfx_point(box.x, box.y), GFX_BUFFER_FRONT))) + { + GFXERROR("Error occured while updating region (%d,%d,%d,%d) in buffer %d\n", box.x, box.y, box.xl, box.yl, GFX_BUFFER_BACK); + } +} + +int +game_select_display(gfx_driver_t *gfx_driver, game_t *game_list, int game_count, gfx_bitmap_font_t* font_default, gfx_bitmap_font_t* font_small) +{ + int error; + int cont; + sci_event_t event; + int focus_button = GS_BUTTON_PLAY; + int selected_game = 0; + int first_game = 0; + int max_game; + rect_t box; + + box = gfx_rect(0, 0, 320 * gfx_driver->mode->xfact, 200 * gfx_driver->mode->yfact); + + max_game = (int)(((double)GS_LISTBOX_CONTENTS_HEIGHT) / font_small->line_height); + + game_select_display_game_list(gfx_driver, font_default, font_small, game_list, game_count, first_game, selected_game, focus_button); + if ((error = gfx_driver->update(gfx_driver, box, gfx_point(box.x, box.y), GFX_BUFFER_FRONT))) + { + GFXERROR("Error occured while updating region (%d,%d,%d,%d) in buffer %d\n", box.x, box.y, box.xl, box.yl, GFX_BUFFER_BACK); + } + + cont = 2; + + while (cont) + { + event = game_select_gfxop_get_event(gfx_driver, SCI_EVT_KEYBOARD); + + if (event.type == SCI_EVT_KEYBOARD) + { + if (event.data == SCI_K_ENTER) + { + switch (focus_button) + { + case GS_BUTTON_PLAY: + cont = 0; + break; + case GS_BUTTON_QUIT: + cont = 0; + selected_game = -1; + break; + } + } + else if ((event.buckybits & (SCI_K_TAB | SCI_EVM_RSHIFT)) || (event.buckybits & (SCI_K_TAB | SCI_EVM_LSHIFT))) + { + focus_button--; + + if (focus_button < GS_BUTTON_FIRST) + focus_button = GS_BUTTON_LAST; + + game_select_display_game_list(gfx_driver, font_default, font_small, game_list, game_count, first_game, selected_game, focus_button); + if ((error = gfx_driver->update(gfx_driver, box, gfx_point(box.x, box.y), GFX_BUFFER_FRONT))) + { + GFXERROR("Error occured while updating region (%d,%d,%d,%d) in buffer %d\n", box.x, box.y, box.xl, box.yl, GFX_BUFFER_BACK); + } + } + else if (event.data == SCI_K_TAB) + { + focus_button++; + + if (focus_button > GS_BUTTON_LAST) + focus_button = GS_BUTTON_FIRST; + + game_select_display_game_list(gfx_driver, font_default, font_small, game_list, game_count, first_game, selected_game, focus_button); + if ((error = gfx_driver->update(gfx_driver, box, gfx_point(box.x, box.y), GFX_BUFFER_FRONT))) + { + GFXERROR("Error occured while updating region (%d,%d,%d,%d) in buffer %d\n", box.x, box.y, box.xl, box.yl, GFX_BUFFER_BACK); + } + } + else if (event.data == SCI_K_ESC) + { + cont = 0; + selected_game = -1; + } + else if (event.data == SCI_K_UP) + { + if (selected_game > 0) + { + selected_game--; + + focus_button = GS_BUTTON_PLAY; + + if (selected_game < first_game) + { + first_game = selected_game; + } + + game_select_display_game_list(gfx_driver, font_default, font_small, game_list, game_count, first_game, selected_game, focus_button); + if ((error = gfx_driver->update(gfx_driver, box, gfx_point(box.x, box.y), GFX_BUFFER_FRONT))) + { + GFXERROR("Error occured while updating region (%d,%d,%d,%d) in buffer %d\n", box.x, box.y, box.xl, box.yl, GFX_BUFFER_BACK); + } + } + } + else if (event.data == SCI_K_DOWN) + { + if (selected_game < game_count - 1) + { + selected_game++; + + focus_button = GS_BUTTON_PLAY; + + if (selected_game - first_game + 1 > max_game) + { + first_game = selected_game - max_game + 1; + } + + game_select_display_game_list(gfx_driver, font_default, font_small, game_list, game_count, first_game, selected_game, focus_button); + if ((error = gfx_driver->update(gfx_driver, box, gfx_point(box.x, box.y), GFX_BUFFER_FRONT))) + { + GFXERROR("Error occured while updating region (%d,%d,%d,%d) in buffer %d\n", box.x, box.y, box.xl, box.yl, GFX_BUFFER_BACK); + } + } + } + else if (event.data == SCI_K_PGUP) + { + selected_game -= (max_game - 1); + if (selected_game < 0) + { + selected_game = 0; + } + + focus_button = GS_BUTTON_PLAY; + + if (selected_game < first_game) + { + first_game = selected_game; + } + + game_select_display_game_list(gfx_driver, font_default, font_small, game_list, game_count, first_game, selected_game, focus_button); + if ((error = gfx_driver->update(gfx_driver, box, gfx_point(box.x, box.y), GFX_BUFFER_FRONT))) + { + GFXERROR("Error occured while updating region (%d,%d,%d,%d) in buffer %d\n", box.x, box.y, box.xl, box.yl, GFX_BUFFER_BACK); + } + } + else if (event.data == SCI_K_PGDOWN) + { + selected_game += max_game - 1; + if (selected_game >= game_count) + { + selected_game = game_count - 1; + } + + focus_button = GS_BUTTON_PLAY; + + if (selected_game - first_game + 1 > max_game) + { + first_game = selected_game - max_game + 1; + } + + game_select_display_game_list(gfx_driver, font_default, font_small, game_list, game_count, first_game, selected_game, focus_button); + if ((error = gfx_driver->update(gfx_driver, box, gfx_point(box.x, box.y), GFX_BUFFER_FRONT))) + { + GFXERROR("Error occured while updating region (%d,%d,%d,%d) in buffer %d\n", box.x, box.y, box.xl, box.yl, GFX_BUFFER_BACK); + } + } + } + + game_select_gfxop_usleep(gfx_driver, 25000); + } + + return selected_game; +} diff --git a/engines/sci/module.mk b/engines/sci/module.mk new file mode 100644 index 0000000000..6f8d5da745 --- /dev/null +++ b/engines/sci/module.mk @@ -0,0 +1,111 @@ +MODULE := engines/sci + +MODULE_OBJS = \ + engine/game.o \ + engine/gc.o \ + engine/grammar.o \ + engine/kernel.o \ + engine/kevent.o \ + engine/kfile.o \ + engine/kgraphics.o \ + engine/klists.o \ + engine/kmath.o \ + engine/kmenu.o \ + engine/kmovement.o \ + engine/kpathing.o \ + engine/kscripts.o \ + engine/ksound.o \ + engine/kstring.o \ + engine/said.o \ + engine/savegame.o \ + engine/scriptconsole.o \ + engine/scriptdebug.o \ + engine/seg_manager.o \ + engine/sys_strings.o \ + engine/vm.o \ + gfx/antialias.o \ + gfx/font.o \ + gfx/font-5x8.o \ + gfx/font-6x10.o \ + gfx/gfx_res_options.o \ + gfx/gfx_resource.o \ + gfx/gfx_support.o \ + gfx/gfx_tools.o \ + gfx/menubar.o \ + gfx/operations.o \ + gfx/resmgr.o \ + gfx/sbtree.o \ + gfx/sci_widgets.o \ + gfx/widgets.o \ + gfx/drivers/scummvm_driver.o \ + gfx/resource/sci_cursor_0.o \ + gfx/resource/sci_font.o \ + gfx/resource/sci_pal_1.o \ + gfx/resource/sci_pic_0.o \ + gfx/resource/sci_resmgr.o \ + gfx/resource/sci_view_0.o \ + gfx/resource/sci_view_1.o \ + scicore/aatree.o \ + scicore/console.o \ + scicore/decompress0.o \ + scicore/decompress01.o \ + scicore/decompress1.o \ + scicore/decompress11.o \ + scicore/exe.o \ + scicore/exe_lzexe.o \ + scicore/exe_raw.o \ + scicore/int_hashmap.o \ + scicore/reg_t_hashmap.o \ + scicore/resource.o \ + scicore/resource_map.o \ + scicore/resource_patch.o \ + scicore/sci_memory.o \ + scicore/script.o \ + scicore/tools.o \ + scicore/versions.o \ + scicore/vocab.o \ + scicore/vocab_debug.o \ + scummvm/detection.o \ + scummvm/scummvm_engine.o \ + sfx/adlib.o \ + sfx/core.o \ + sfx/iterator.o \ + sfx/pcm-iterator.o \ + sfx/songlib.o \ + sfx/time.o \ + sfx/device/devices.o \ + sfx/mixer/mixers.o \ + sfx/mixer/soft.o \ + sfx/pcm_device/pcm_devices.o \ + sfx/pcm_device/scummvm.o \ + sfx/player/players.o \ + sfx/player/polled.o \ + sfx/player/realtime.o \ + sfx/seq/sequencers.o \ + sfx/softseq/amiga.o \ + sfx/softseq/fmopl.o \ + sfx/softseq/opl2.o \ + sfx/softseq/pcspeaker.o \ + sfx/softseq/SN76496.o \ + sfx/softseq/softsequencers.o \ + sfx/timer/scummvm.o \ + sfx/timer/timers.o + +CPPFLAGS+=-DSCUMMVM -I$(srcdir)/engines/sci/src/include + +# Build .c files as C++ +%.o: %.c + $(MKDIR) $(*D)/$(DEPDIR) + $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o + +# Generate savegame.cpp +$(srcdir)/engines/sci/src/engine/savegame.cpp: $(srcdir)/engines/sci/src/engine/savegame.cfsml + cat $< | perl $(srcdir)/engines/sci/engine/cfsml.pl -f savegame.cfsml > $@ + +# This module can be built as a plugin +ifeq ($(ENABLE_SCI), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/sci/scicore/Makefile.am b/engines/sci/scicore/Makefile.am new file mode 100644 index 0000000000..dfc61adbaa --- /dev/null +++ b/engines/sci/scicore/Makefile.am @@ -0,0 +1,11 @@ +INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@ +EXTRA_DIST = sci_dos.c treedef.1 treedef.2 treedef.3 hufftree.1 \ + hufftree.2 hufftree.3 huffmake.pl \ + hashmap.c exe.h exe_dec.h games.h +noinst_LIBRARIES = libscicore.a +libscicore_a_SOURCES = aatree.c tools.c resource.c decompress0.c \ + versions.c decompress01.c decompress1.c decompress11.c \ + script.c vocab.c vocab_debug.c old_objects.c modules.c \ + sci_memory.c resource_map.c resource_patch.c \ + fnmatch.c int_hashmap.c console.c exe.c exe_lzexe.c \ + exe_raw.c reg_t_hashmap.c diff --git a/engines/sci/scicore/aatree.c b/engines/sci/scicore/aatree.c new file mode 100644 index 0000000000..74c8f6519e --- /dev/null +++ b/engines/sci/scicore/aatree.c @@ -0,0 +1,181 @@ +/*************************************************************************** + aatree.c Copyright (C) 2006 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik [w.f.b.w.v.niftrik@stud.tue.nl] + +***************************************************************************/ + +#include +#include + +#include + +struct aatree +{ + struct aatree *left, *right; + int level; + void *key; +}; + +/* Sentinel node */ +static aatree_t bottom = {&bottom, &bottom, 0, NULL}; + +static void +skew(aatree_t **t) +{ + if ((*t)->left->level == (*t)->level) { + /* Rotate right */ + aatree_t *temp = *t; + *t = (*t)->left; + temp->left = (*t)->right; + (*t)->right = temp; + } +} + +static void +split(aatree_t **t) +{ + if ((*t)->right->right->level == (*t)->level) { + /* Rotate left */ + aatree_t *temp = *t; + *t = (*t)->right; + temp->right = (*t)->left; + (*t)->left = temp; + (*t)->level++; + } +} + +static int +delete_node(void *x, aatree_t **t, aatree_t *deleted, int (*compar)(const void *, const void *)) +{ + int retval = -1; + + if (*t != &bottom) { + /* Search down the tree */ + aatree_t **n; + + if (compar(x, (*t)->key) < 0) + n = &(*t)->left; + else { + n = &(*t)->right; + deleted = *t; + } + + retval = delete_node(x, n, deleted, compar); + + /* At the bottom of the tree we remove the element (if it is present) */ + if ((*n == &bottom) && (deleted != &bottom) && (compar(x, deleted->key) == 0)) { + aatree_t *temp; + deleted->key = (*t)->key; + temp = *t; + *t = (*t)->right; + sci_free(temp); + retval = 0; + } + else if (((*t)->left->level < (*t)->level - 1) || ((*t)->right->level < (*t)->level - 1)) { + (*t)->level--; + if ((*t)->right->level > (*t)->level) + (*t)->right->level = (*t)->level; + skew(t); + skew(&(*t)->right); + skew(&(*t)->right->right); + split(t); + split(&(*t)->right); + } + } + + return retval; +} + +aatree_t * +aatree_new() +{ + return ⊥ +} + +int +aatree_insert(void *x, aatree_t **t, int (*compar)(const void *, const void *)) +{ + int retval = -1; + int c; + + if (*t == &bottom) { + *t = (aatree_t*)sci_malloc(sizeof(aatree_t)); + + if (*t == NULL) + return 1; + + (*t)->key = x; + (*t)->left = ⊥ + (*t)->right = ⊥ + (*t)->level = 1; + return 0; + } + + c = compar(x, (*t)->key); + + if (c < 0) + retval = aatree_insert(x, &(*t)->left, compar); + else if (c > 0) + retval = aatree_insert(x, &(*t)->right, compar); + + skew(t); + split(t); + return retval; +} + +int +aatree_delete(void *x, aatree_t **t, int (*compar)(const void *, const void *)) +{ + return delete_node(x, t, &bottom, compar); +} + +aatree_t * +aatree_walk(aatree_t *t, int direction) +{ + if ((direction == AATREE_WALK_LEFT) && (t->left != &bottom)) + return t->left; + + if ((direction == AATREE_WALK_RIGHT) && (t->right != &bottom)) + return t->right; + + return NULL; +} + +void * +aatree_get_data(aatree_t *t) +{ + return t->key; +} + +void +aatree_free(aatree_t *t) +{ + if (t == &bottom) + return; + + aatree_free(t->left); + aatree_free(t->right); + + sci_free(t); +} diff --git a/engines/sci/scicore/console.c b/engines/sci/scicore/console.c new file mode 100644 index 0000000000..655cbbb66e --- /dev/null +++ b/engines/sci/scicore/console.c @@ -0,0 +1,151 @@ +/*************************************************************************** + console.c Copyright (C) 1999..2002 Christoph Reichenbach, TU Darmstadt + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + +***************************************************************************/ +/* First part of the console implmentation: VM independent stuff */ +/* Remember, it doesn't have to be fast. */ + +#include +#include +#ifdef SCI_CONSOLE + +int con_passthrough = 0; +FILE *con_file = NULL; + +static void(*_con_string_callback)(char*) = NULL; +static void (*_con_pixmap_callback)(gfx_pixmap_t *) = NULL; + + +/****************************************/ +/* sciprintf */ +/****************************************/ + + +int +sciprintf (const char *fmt, ...) +{ + va_list argp; + size_t bufsize = 256; + int i; + char *buf = (char *) sci_malloc (bufsize); + + if (NULL == fmt) { + fprintf(stderr, "console.c: sciprintf(): NULL passed for parameter fmt\n"); + return -1; + } + + if (NULL == buf) { + fprintf(stderr, "console.c: sciprintf(): malloc failed for buf\n"); + return -1; + } + + va_start (argp, fmt); + while ((i = vsnprintf (buf, bufsize - 1, fmt, argp)) == -1 + || (i >= bufsize - 2)) { + /* while we're out of space... */ + va_end (argp); + va_start (argp, fmt); /* reset argp */ + + free (buf); + buf = (char *) sci_malloc (bufsize <<= 1); + } + va_end (argp); + + if (con_passthrough) + printf ("%s", buf); + if (con_file) + fprintf (con_file, "%s", buf); + + + if (_con_string_callback) + _con_string_callback(buf); + else + free(buf); + + return 1; +} + +void +con_set_string_callback(void(*callback)(char *)) +{ + _con_string_callback = callback; +} + +void +con_set_pixmap_callback(void(*callback)(gfx_pixmap_t *)) +{ + _con_pixmap_callback = callback; +} + +int +con_can_handle_pixmaps(void) +{ + return _con_pixmap_callback != NULL; +} + +int +con_insert_pixmap(gfx_pixmap_t *pixmap) +{ + if (_con_pixmap_callback) + _con_pixmap_callback(pixmap); + else + return 1; + return 0; +} + + +void +open_console_file (char *filename) +{ + if (con_file != NULL) + fclose (con_file); + + if (NULL == filename) + { + fprintf(stderr, "console.c: open_console_file(): NULL passed for parameter filename\r\n"); + } +#ifdef WIN32 + con_file = fopen (filename, "wt"); +#else + con_file = fopen (filename, "w"); +#endif + + if (NULL == con_file) + fprintf(stderr, "console.c: open_console_file(): Could not open output file %s\n", filename); + +} + +void +close_console_file (void) +{ + if (con_file != NULL) + { + fclose (con_file); + con_file = NULL; + } +} + + +#endif /* SCI_CONSOLE */ diff --git a/engines/sci/scicore/decompress0.c b/engines/sci/scicore/decompress0.c new file mode 100644 index 0000000000..00c98e03df --- /dev/null +++ b/engines/sci/scicore/decompress0.c @@ -0,0 +1,372 @@ +/*************************************************************************** + decompress0.c Copyright (C) 1999 Christoph Reichenbach, TU Darmstadt + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + +***************************************************************************/ +/* Reads data from a resource file and stores the result in memory. +** This is for SCI version 0 style compression. +*/ + +#include +#include + +/* #define _SCI_DECOMPRESS_DEBUG */ + +/* 9-12 bit LZW encoding */ +int +decrypt1(guint8 *dest, guint8 *src, int length, int complength) + /* Doesn't do length checking yet */ +{ + /* Theory: Considering the input as a bit stream, we get a series of + ** 9 bit elements in the beginning. Every one of them is a 'token' + ** and either represents a literal (if < 0x100), or a link to a previous + ** token (tokens start at 0x102, because 0x101 is the end-of-stream + ** indicator and 0x100 is used to reset the bit stream decoder). + ** If it's a link, the indicated token and the character following it are + ** placed into the output stream. Note that the 'indicated token' may + ** very well consist of a link-token-plus-literal construct again, so + ** it's possible to represent strings longer than 2 recursively. + ** If the maximum number of tokens has been reached, the bit length is + ** increased by one, up to a maximum of 12 bits. + ** This implementation remembers the position each token was print to in + ** the output array, and the length of this token. This method should + ** be faster than the recursive approach. + */ + + guint16 bitlen = 9; /* no. of bits to read (max. 12) */ + guint16 bitmask = 0x01ff; + guint16 bitctr = 0; /* current bit position */ + guint16 bytectr = 0; /* current byte position */ + guint16 token; /* The last received value */ + guint16 maxtoken = 0x200; /* The biggest token */ + + guint16 tokenlist[4096]; /* pointers to dest[] */ + guint16 tokenlengthlist[4096]; /* char length of each token */ + guint16 tokenctr = 0x102; /* no. of registered tokens (starts here)*/ + + guint16 tokenlastlength = 0; + + guint16 destctr = 0; + + while (bytectr < complength) { + + guint32 tokenmaker = src[bytectr++] >> bitctr; + if (bytectr < complength) + tokenmaker |= (src[bytectr] << (8-bitctr)); + if (bytectr+1 < complength) + tokenmaker |= (src[bytectr+1] << (16-bitctr)); + + token = tokenmaker & bitmask; + + bitctr += bitlen - 8; + + while (bitctr >= 8) { + bitctr -= 8; + bytectr++; + } + + if (token == 0x101) return 0; /* terminator */ + if (token == 0x100) { /* reset command */ + maxtoken = 0x200; + bitlen = 9; + bitmask = 0x01ff; + tokenctr = 0x0102; + } else { + + { + int i; + + if (token > 0xff) { + if (token >= tokenctr) + { +#ifdef _SCI_DECOMPRESS_DEBUG + fprintf(stderr, "decrypt1: Bad token %x!\n", token); +#endif + /* Well this is really bad */ + /* May be it should throw something like SCI_ERROR_DECOMPRESSION_INSANE */ + } else + { + tokenlastlength = tokenlengthlist[token]+1; + if (destctr+tokenlastlength>length) + { +#ifdef _SCI_DECOMPRESS_DEBUG + + /* For me this seems a normal situation, It's necessary to handle it*/ + printf ("decrypt1: Trying to write beyond the end of array(len=%d, destctr=%d, tok_len=%d)!\n", + length, destctr, tokenlastlength); +#endif + + i = 0; + for (; destctr= length) + { +#ifdef _SCI_DECOMPRESS_DEBUG + printf ("decrypt1: Try to write single byte beyond end of array!\n"); +#endif + } else + dest[destctr++] = (byte)token; + } + + } + + if (tokenctr == maxtoken) { + if (bitlen < 12) { + bitlen++; + bitmask <<= 1; + bitmask |= 1; + maxtoken <<= 1; + } else continue; /* no further tokens allowed */ + } + + tokenlist[tokenctr] = destctr-tokenlastlength; + tokenlengthlist[tokenctr++] = tokenlastlength; + + } + + } + + return 0; + +} + + +/* Huffman-style token encoding */ +/***************************************************************************/ +/* This code was taken from Carl Muckenhoupt's sde.c, with some minor */ +/* modifications. */ +/***************************************************************************/ + +/* decrypt2 helper function */ +gint16 getc2(guint8 *node, guint8 *src, + guint16 *bytectr, guint16 *bitctr, int complength) +{ + guint16 next; + + while (node[1] != 0) { + gint16 value = (src[*bytectr] << (*bitctr)); + (*bitctr)++; + if (*bitctr == 8) { + (*bitctr) = 0; + (*bytectr)++; + } + + if (value & 0x80) { + next = node[1] & 0x0f; /* low 4 bits */ + if (next == 0) { + guint16 result = (src[*bytectr] << (*bitctr)); + + if (++(*bytectr) > complength) + return -1; + else if (*bytectr < complength) + result |= src[*bytectr] >> (8-(*bitctr)); + + result &= 0x0ff; + return (result | 0x100); + } + } + else { + next = node[1] >> 4; /* high 4 bits */ + } + node += next<<1; + } + return getInt16(node); +} + +/* Huffman token decryptor */ +int decrypt2(guint8* dest, guint8* src, int length, int complength) + /* no complength checking atm */ +{ + guint8 numnodes, terminator; + guint8 *nodes; + gint16 c; + guint16 bitctr = 0, bytectr; + + numnodes = src[0]; + terminator = src[1]; + bytectr = 2+ (numnodes << 1); + nodes = src+2; + + while (((c = getc2(nodes, src, &bytectr, &bitctr, complength)) + != (0x0100 | terminator)) && (c >= 0)) { + if (length-- == 0) return SCI_ERROR_DECOMPRESSION_OVERFLOW; + + *dest = (guint8)c; + dest++; + } + + return (c == -1) ? SCI_ERROR_DECOMPRESSION_OVERFLOW : 0; + +} +/***************************************************************************/ +/* Carl Muckenhoupt's decompression code ends here */ +/***************************************************************************/ + +int sci0_get_compression_method(int resh) +{ + guint16 compressedLength; + guint16 compressionMethod; + guint16 result_size; + + /* Dummy variable */ + if (read(resh, &result_size, 2) != 2) + return SCI_ERROR_IO_ERROR; + + if ((read(resh, &compressedLength, 2) != 2) || + (read(resh, &result_size, 2) != 2) || + (read(resh, &compressionMethod, 2) != 2)) + return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN + compressionMethod = GUINT16_SWAP_LE_BE_CONSTANT(compressionMethod); +#endif + + return compressionMethod; +} + + +int decompress0(resource_t *result, int resh, int sci_version) +{ + guint16 compressedLength; + guint16 compressionMethod; + guint16 result_size; + guint8 *buffer; + + if (read(resh, &(result->id),2) != 2) + return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN + result->id = GUINT16_SWAP_LE_BE_CONSTANT(result->id); +#endif + result->number = result->id & 0x07ff; + result->type = result->id >> 11; + + if ((result->number > sci_max_resource_nr[sci_version]) || (result->type > sci_invalid_resource)) + return SCI_ERROR_DECOMPRESSION_INSANE; + + if ((read(resh, &compressedLength, 2) != 2) || + (read(resh, &result_size, 2) != 2) || + (read(resh, &compressionMethod, 2) != 2)) + return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN + compressedLength = GUINT16_SWAP_LE_BE_CONSTANT(compressedLength); + result_size = GUINT16_SWAP_LE_BE_CONSTANT(result_size); + compressionMethod = GUINT16_SWAP_LE_BE_CONSTANT(compressionMethod); +#endif + result->size = result_size; + + if ((result->size > SCI_MAX_RESOURCE_SIZE) || + (compressedLength > SCI_MAX_RESOURCE_SIZE)) + return SCI_ERROR_RESOURCE_TOO_BIG; + /* With SCI0, this simply cannot happen. */ + + if (compressedLength > 4) + compressedLength -= 4; + else { /* Object has size zero (e.g. view.000 in sq3) (does this really exist?) */ + result->data = 0; + result->status = SCI_STATUS_NOMALLOC; + return SCI_ERROR_EMPTY_OBJECT; + } + + buffer = (guint8*)sci_malloc(compressedLength); + result->data = (unsigned char*)sci_malloc(result->size); + + if (read(resh, buffer, compressedLength) != compressedLength) { + free(result->data); + free(buffer); + return SCI_ERROR_IO_ERROR; + }; + + +#ifdef _SCI_DECOMPRESS_DEBUG + fprintf(stderr, "Resource %s.%03hi encrypted with method %hi at %.2f%%" + " ratio\n", + sci_resource_types[result->type], result->number, compressionMethod, + (result->size == 0)? -1.0 : + (100.0 * compressedLength / result->size)); + fprintf(stderr, " compressedLength = 0x%hx, actualLength=0x%hx\n", + compressedLength, result->size); +#endif + + switch(compressionMethod) { + + case 0: /* no compression */ + if (result->size != compressedLength) { + free(result->data); + result->data = NULL; + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_DECOMPRESSION_OVERFLOW; + } + memcpy(result->data, buffer, compressedLength); + result->status = SCI_STATUS_ALLOCATED; + break; + + case 1: /* LZW compression */ + if (decrypt1(result->data, buffer, result->size, compressedLength)) { + free(result->data); + result->data = 0; /* So that we know that it didn't work */ + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_DECOMPRESSION_OVERFLOW; + } + result->status = SCI_STATUS_ALLOCATED; + break; + + case 2: /* Some sort of Huffman encoding */ + if (decrypt2(result->data, buffer, result->size, compressedLength)) { + free(result->data); + result->data = 0; /* So that we know that it didn't work */ + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_DECOMPRESSION_OVERFLOW; + } + result->status = SCI_STATUS_ALLOCATED; + break; + + default: + fprintf(stderr,"Resource %s.%03hi: Compression method %hi not " + "supported!\n", sci_resource_types[result->type], result->number, + compressionMethod); + free(result->data); + result->data = 0; /* So that we know that it didn't work */ + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_UNKNOWN_COMPRESSION; + } + + free(buffer); + return 0; +} + diff --git a/engines/sci/scicore/decompress01.c b/engines/sci/scicore/decompress01.c new file mode 100644 index 0000000000..69795b1c6d --- /dev/null +++ b/engines/sci/scicore/decompress01.c @@ -0,0 +1,655 @@ +/*************************************************************************** + decompress01.c Copyright (C) 1999 The FreeSCI project + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + +***************************************************************************/ +/* Reads data from a resource file and stores the result in memory */ + +#include +#include + +/*************************************************************************** +* The following code was originally created by Carl Muckenhoupt for his +* SCI decoder. It has been ported to the FreeSCI environment by Sergey Lapin. +***************************************************************************/ + +/* TODO: Clean up, re-organize, improve speed-wise */ + +struct tokenlist { + guint8 data; + gint16 next; +} tokens[0x1004]; + +static gint8 stak[0x1014] = {0}; +static gint8 lastchar = 0; +static gint16 stakptr = 0; +static guint16 numbits, bitstring, lastbits, decryptstart; +static gint16 curtoken, endtoken; + + +guint32 gbits(int numbits, guint8 * data, int dlen); + +void decryptinit3(void) +{ + int i; + lastchar = lastbits = bitstring = stakptr = 0; + numbits = 9; + curtoken = 0x102; + endtoken = 0x1ff; + decryptstart = 0; + gbits(0,0,0); + for(i=0;i<0x1004;i++) { + tokens[i].next=0; + tokens[i].data=0; + } +} + +int decrypt3(guint8 *dest, guint8 *src, int length, int complength) +{ + static gint16 token; + while(length != 0) { + + switch (decryptstart) { + case 0: + case 1: + bitstring = gbits(numbits, src, complength); + if (bitstring == 0x101) { /* found end-of-data signal */ + decryptstart = 4; + return 0; + } + if (decryptstart == 0) { /* first char */ + decryptstart = 1; + lastbits = bitstring; + *(dest++) = lastchar = (bitstring & 0xff); + if (--length != 0) continue; + return 0; + } + if (bitstring == 0x100) { /* start-over signal */ + numbits = 9; + endtoken = 0x1ff; + curtoken = 0x102; + decryptstart = 0; + continue; + } + token = bitstring; + if (token >= curtoken) { /* index past current point */ + token = lastbits; + stak[stakptr++] = lastchar; + } + while ((token > 0xff)&&(token < 0x1004)) { /* follow links back in data */ + stak[stakptr++] = tokens[token].data; + token = tokens[token].next; + } + lastchar = stak[stakptr++] = token & 0xff; + case 2: + while (stakptr > 0) { /* put stack in buffer */ + *(dest++) = stak[--stakptr]; + length--; + if (length == 0) { + decryptstart = 2; + return 0; + } + } + decryptstart = 1; + if (curtoken <= endtoken) { /* put token into record */ + tokens[curtoken].data = lastchar; + tokens[curtoken].next = lastbits; + curtoken++; + if (curtoken == endtoken && numbits != 12) { + numbits++; + endtoken <<= 1; + endtoken++; + } + } + lastbits = bitstring; + continue; /* When are "break" and "continue" synonymous? */ + case 4: + return 0; + } + } + return 0; /* [DJ] shut up compiler warning */ +} + +guint32 gbits(int numbits, guint8 * data, int dlen) +{ + int place; /* indicates location within byte */ + guint32 bitstring; + static guint32 whichbit=0; + int i; + + if(numbits==0) {whichbit=0; return 0;} + + place = whichbit >> 3; + bitstring=0; + for(i=(numbits>>3)+1;i>=0;i--) + { + if (i+place < dlen) + bitstring |=data[place+i] << (8*(2-i)); + } + /* bitstring = data[place+2] | (long)(data[place+1])<<8 + | (long)(data[place])<<16;*/ + bitstring >>= 24-(whichbit & 7)-numbits; + bitstring &= (0xffffffff >> (32-numbits)); + /* Okay, so this could be made faster with a table lookup. + It doesn't matter. It's fast enough as it is. */ + whichbit += numbits; + return bitstring; +} + +/*************************************************************************** +* Carl Muckenhoupt's code ends here +***************************************************************************/ + +enum { + PIC_OP_SET_COLOR = 0xf0, + PIC_OP_DISABLE_VISUAL = 0xf1, + PIC_OP_SET_PRIORITY = 0xf2, + PIC_OP_DISABLE_PRIORITY = 0xf3, + PIC_OP_SHORT_PATTERNS = 0xf4, + PIC_OP_MEDIUM_LINES = 0xf5, + PIC_OP_LONG_LINES = 0xf6, + PIC_OP_SHORT_LINES = 0xf7, + PIC_OP_FILL = 0xf8, + PIC_OP_SET_PATTERN = 0xf9, + PIC_OP_ABSOLUTE_PATTERN = 0xfa, + PIC_OP_SET_CONTROL = 0xfb, + PIC_OP_DISABLE_CONTROL = 0xfc, + PIC_OP_MEDIUM_PATTERNS = 0xfd, + PIC_OP_OPX = 0xfe, + PIC_OP_TERMINATE = 0xff +}; + +enum { + PIC_OPX_SET_PALETTE_ENTRIES = 0, + PIC_OPX_EMBEDDED_VIEW = 1, + PIC_OPX_SET_PALETTE = 2, + PIC_OPX_PRIORITY_TABLE_EQDIST = 3, + PIC_OPX_PRIORITY_TABLE_EXPLICIT = 4 +}; + +#define PAL_SIZE 1284 +#define CEL_HEADER_SIZE 7 +#define EXTRA_MAGIC_SIZE 15 + +static +void decode_rle(byte **rledata, byte **pixeldata, byte *outbuffer, int size) +{ + int pos = 0; + char nextbyte; + byte *rd = *rledata; + byte *ob = outbuffer; + byte *pd = *pixeldata; + + while (pos < size) + { + nextbyte = *(rd++); + *(ob++) = nextbyte; + pos ++; + switch (nextbyte&0xC0) + { + case 0x40 : + case 0x00 : + memcpy(ob, pd, nextbyte); + pd +=nextbyte; + ob += nextbyte; + pos += nextbyte; + break; + case 0xC0 : + break; + case 0x80 : + nextbyte = *(pd++); + *(ob++) = nextbyte; + pos ++; + break; + } + } + + *rledata = rd; + *pixeldata = pd; +} + +/* + * Does the same this as above, only to determine the length of the compressed + * source data. + * + * Yes, this is inefficient. + */ +static +int rle_size(byte *rledata, int dsize) +{ + int pos = 0; + char nextbyte; + int size = 0; + + while (pos < dsize) + { + nextbyte = *(rledata++); + pos ++; + size ++; + + switch (nextbyte&0xC0) + { + case 0x40 : + case 0x00 : + pos += nextbyte; + break; + case 0xC0 : + break; + case 0x80 : + pos ++; + break; + } + } + + return size; +} + +byte *pic_reorder(byte *inbuffer, int dsize) +{ + byte *reorderBuffer; + int view_size; + int view_start; + int cdata_size; + int i; + byte *seeker = inbuffer; + byte *writer; + char viewdata[CEL_HEADER_SIZE]; + byte *cdata, *cdata_start; + + writer = reorderBuffer=(byte *) malloc(dsize); + + *(writer++) = PIC_OP_OPX; + *(writer++) = PIC_OPX_SET_PALETTE; + + for (i=0;i<256;i++) /* Palette translation map */ + *(writer++) = i; + + putInt16(writer, 0); /* Palette stamp */ + writer += 2; + putInt16(writer, 0); + writer += 2; + + view_size = getUInt16(seeker); + seeker += 2; + view_start = getUInt16(seeker); + seeker += 2; + cdata_size = getUInt16(seeker); + seeker += 2; + + memcpy(viewdata, seeker, sizeof(viewdata)); + seeker += sizeof(viewdata); + + memcpy(writer, seeker, 4*256); /* Palette */ + seeker += 4*256; + writer += 4*256; + + if (view_start != PAL_SIZE + 2) /* +2 for the opcode */ + { + memcpy(writer, seeker, view_start-PAL_SIZE-2); + seeker += view_start - PAL_SIZE - 2; + writer += view_start - PAL_SIZE - 2; + } + + if (dsize != view_start+EXTRA_MAGIC_SIZE+view_size) + { + memcpy(reorderBuffer+view_size+view_start+EXTRA_MAGIC_SIZE, seeker, + dsize-view_size-view_start-EXTRA_MAGIC_SIZE); + seeker += dsize-view_size-view_start-EXTRA_MAGIC_SIZE; + } + + cdata_start=cdata=(byte *) malloc(cdata_size); + memcpy(cdata, seeker, cdata_size); + seeker += cdata_size; + + writer = reorderBuffer + view_start; + *(writer++) = PIC_OP_OPX; + *(writer++) = PIC_OPX_EMBEDDED_VIEW; + *(writer++) = 0; + *(writer++) = 0; + *(writer++) = 0; + putInt16(writer, view_size + 8); + writer += 2; + + memcpy(writer, viewdata, sizeof(viewdata)); + writer += sizeof(viewdata); + + *(writer++) = 0; + + decode_rle(&seeker, &cdata, writer, view_size); + + free(cdata_start); + free(inbuffer); + return reorderBuffer; +} + +#define VIEW_HEADER_COLORS_8BIT 0x80 + +static +void build_cel_headers(byte **seeker, byte **writer, int celindex, int *cc_lengths, int max) +{ + int c, w; + + for (c=0;cid),2) != 2) + return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN + result->id = GUINT16_SWAP_LE_BE_CONSTANT(result->id); +#endif + + result->number = result->id & 0x07ff; + result->type = result->id >> 11; + + if ((result->number > sci_max_resource_nr[sci_version] || (result->type > sci_invalid_resource))) + return SCI_ERROR_DECOMPRESSION_INSANE; + + if ((read(resh, &compressedLength, 2) != 2) || + (read(resh, &result_size, 2) != 2) || + (read(resh, &compressionMethod, 2) != 2)) + return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN + compressedLength = GUINT16_SWAP_LE_BE_CONSTANT(compressedLength); + result_size = GUINT16_SWAP_LE_BE_CONSTANT(result_size); + compressionMethod = GUINT16_SWAP_LE_BE_CONSTANT(compressionMethod); +#endif + result->size = result_size; + + /* if ((result->size < 0) || (compressedLength < 0)) + return SCI_ERROR_DECOMPRESSION_INSANE; */ + /* This return will never happen in SCI0 or SCI1 (does it have any use?) */ + + if ((result->size > SCI_MAX_RESOURCE_SIZE) || + (compressedLength > SCI_MAX_RESOURCE_SIZE)) + return SCI_ERROR_RESOURCE_TOO_BIG; + + if (compressedLength > 4) + compressedLength -= 4; + else { /* Object has size zero (e.g. view.000 in sq3) (does this really exist?) */ + result->data = 0; + result->status = SCI_STATUS_NOMALLOC; + return SCI_ERROR_EMPTY_OBJECT; + } + + buffer = (guint8*)sci_malloc(compressedLength); + result->data = (unsigned char*)sci_malloc(result->size); + + if (read(resh, buffer, compressedLength) != compressedLength) { + free(result->data); + free(buffer); + return SCI_ERROR_IO_ERROR; + }; + + +#ifdef _SCI_DECOMPRESS_DEBUG + fprintf(stderr, "Resource %s.%03hi encrypted with method SCI01/%hi at %.2f%%" + " ratio\n", + sci_resource_types[result->type], result->number, compressionMethod, + (result->size == 0)? -1.0 : + (100.0 * compressedLength / result->size)); + fprintf(stderr, " compressedLength = 0x%hx, actualLength=0x%hx\n", + compressedLength, result->size); +#endif + + switch(compressionMethod) { + + case 0: /* no compression */ + if (result->size != compressedLength) { + free(result->data); + result->data = NULL; + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_DECOMPRESSION_OVERFLOW; + } + memcpy(result->data, buffer, compressedLength); + result->status = SCI_STATUS_ALLOCATED; + break; + + case 1: /* Some huffman encoding */ + if (decrypt2(result->data, buffer, result->size, compressedLength)) { + free(result->data); + result->data = 0; /* So that we know that it didn't work */ + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_DECOMPRESSION_OVERFLOW; + } + result->status = SCI_STATUS_ALLOCATED; + break; + + case 2: /* ??? */ + decryptinit3(); + if (decrypt3(result->data, buffer, result->size, compressedLength)) { + free(result->data); + result->data = 0; /* So that we know that it didn't work */ + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_DECOMPRESSION_OVERFLOW; + } + result->status = SCI_STATUS_ALLOCATED; + break; + + case 3: + decryptinit3(); + if (decrypt3(result->data, buffer, result->size, compressedLength)) { + free(result->data); + result->data = 0; /* So that we know that it didn't work */ + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_DECOMPRESSION_OVERFLOW; + } + result->data = view_reorder(result->data, result->size); + result->status = SCI_STATUS_ALLOCATED; + break; + + case 4: + decryptinit3(); + if (decrypt3(result->data, buffer, result->size, compressedLength)) { + free(result->data); + result->data = 0; /* So that we know that it didn't work */ + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_DECOMPRESSION_OVERFLOW; + } + result->data = pic_reorder(result->data, result->size); + result->status = SCI_STATUS_ALLOCATED; + break; + + default: + fprintf(stderr,"Resource %s.%03hi: Compression method SCI1/%hi not " + "supported!\n", sci_resource_types[result->type], result->number, + compressionMethod); + free(result->data); + result->data = 0; /* So that we know that it didn't work */ + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_UNKNOWN_COMPRESSION; + } + + free(buffer); + return 0; +} + diff --git a/engines/sci/scicore/decompress1.c b/engines/sci/scicore/decompress1.c new file mode 100644 index 0000000000..978a00bf86 --- /dev/null +++ b/engines/sci/scicore/decompress1.c @@ -0,0 +1,443 @@ +/*************************************************************************** + decompress1.c Copyright (C) 1999 The FreeSCI project + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + +***************************************************************************/ +/* Reads data from a resource file and stores the result in memory */ + +#include +#include + + + +/* DEFLATE-DCL +** Refer to the FreeSCI docs for a full description. +*/ + +#define HUFFMAN_LEAF 0x40000000 + +struct bit_read_struct { + int length; + int bitpos; + int bytepos; + byte *data; +}; + +#define BRANCH_SHIFT 12 +#define BRANCH_NODE(pos, left, right) ((left << BRANCH_SHIFT) | (right)), +#define LEAF_NODE(pos, value) ((value) | HUFFMAN_LEAF), + + +static int length_tree[] = { +#include "treedef.1" + 0 /* We need something witout a comma at the end */ +}; + +static int distance_tree[] = { +#include "treedef.2" + 0 /* We need something witout a comma at the end */ +}; + +static int ascii_tree[] = { +#include "treedef.3" + 0 /* We need something witout a comma at the end */ +}; + +#define CALLC(x) { if ((x) == -SCI_ERROR_DECOMPRESSION_OVERFLOW) return -SCI_ERROR_DECOMPRESSION_OVERFLOW; } + +static inline int +getbits_msb_first(struct bit_read_struct *inp, int bits) +{ + int morebytes = (bits + inp->bitpos - 1) >> 3; + int result = 0; + int i; + + if (inp->bytepos + morebytes >= inp->length) { + fprintf(stderr,"read out-of-bounds with bytepos %d + morebytes %d >= length %d\n", + inp->bytepos, morebytes, inp->length); + return -SCI_ERROR_DECOMPRESSION_OVERFLOW; + } + + for (i = 0; i <= morebytes; i++) + result |= (inp->data[inp->bytepos + i]) << (i << 3); + + result >>= inp->bitpos; + result &= ~(~0 << bits); + + inp->bitpos += bits - (morebytes << 3); + inp->bytepos += morebytes; + + return result; +} + +static int DEBUG_DCL_INFLATE = 0; /* FIXME: Make this a define eventually */ + +static inline int +getbits(struct bit_read_struct *inp, int bits) +{ + int morebytes = (bits + inp->bitpos - 1) >> 3; + int result = 0; + int i; + + if (inp->bytepos + morebytes >= inp->length) { + fprintf(stderr,"read out-of-bounds with bytepos %d + morebytes %d >= length %d\n", + inp->bytepos, morebytes, inp->length); + return -SCI_ERROR_DECOMPRESSION_OVERFLOW; + } + + for (i = 0; i <= morebytes; i++) + result |= (inp->data[inp->bytepos + i]) << (i << 3); + + result >>= inp->bitpos; + result &= ~((~0) << bits); + + inp->bitpos += bits - (morebytes << 3); + inp->bytepos += morebytes; + + if (DEBUG_DCL_INFLATE) + fprintf(stderr,"(%d:%04x)", bits, result); + + return result; +} + +static int +huffman_lookup(struct bit_read_struct *inp, int *tree) +{ + int pos = 0; + int bit; + + while (!(tree[pos] & HUFFMAN_LEAF)) { + CALLC(bit = getbits(inp, 1)); + if (DEBUG_DCL_INFLATE) + fprintf(stderr,"[%d]:%d->", pos, bit); + if (bit) + pos = tree[pos] & ~(~0 << BRANCH_SHIFT); + else + pos = tree[pos] >> BRANCH_SHIFT; + } + if (DEBUG_DCL_INFLATE) + fprintf(stderr,"=%02x\n", tree[pos] & 0xffff); + return tree[pos] & 0xffff; +} + +#define VALUE_M(i) ((i == 0)? 7 : (VALUE_M(i - 1) + 2**i)); + +#define DCL_ASCII_MODE 1 + +static int +decrypt4_hdyn(byte *dest, int length, struct bit_read_struct *reader) +{ + int mode, length_param, value, val_length, val_distance; + int write_pos = 0; + int M[] = {0x07, 0x08, 0x0A, 0x0E, 0x16, 0x26, 0x46, 0x86, 0x106}; + + CALLC(mode = getbits(reader, 8)); + CALLC(length_param = getbits(reader, 8)); + + if (mode == DCL_ASCII_MODE) { + fprintf(stderr,"DCL-INFLATE: Warning: Decompressing ASCII mode (untested)\n"); + /* DEBUG_DCL_INFLATE = 1; */ + } else if (mode) { + fprintf(stderr,"DCL-INFLATE: Error: Encountered mode %02x, expected 00 or 01\n", mode); + return 1; + } + + if (DEBUG_DCL_INFLATE) { + int i; + for (i = 0; i < reader->length; i++) { + fprintf(stderr,"%02x ", reader->data[i]); + if (!((i+1) & 0x1f)) + fprintf(stderr,"\n"); + } + + + fprintf(stderr,"\n---\n"); + } + + + if (length_param < 3 || length_param > 6) + fprintf(stderr,"Warning: Unexpected length_param value %d (expected in [3,6])\n", length_param); + + while (write_pos < length) { + CALLC(value = getbits(reader, 1)); + + if (value) { /* (length,distance) pair */ + CALLC(value = huffman_lookup(reader, length_tree)); + + if (value < 8) + val_length = value + 2; + else { + int length_bonus; + + val_length = M[value - 7] + 2; + CALLC(length_bonus = getbits(reader, value - 7)); + val_length += length_bonus; + } + + if (DEBUG_DCL_INFLATE) + fprintf(stderr," | "); + + CALLC(value = huffman_lookup(reader, distance_tree)); + + if (val_length == 2) { + val_distance = value << 2; + + CALLC(value = getbits(reader, 2)); + val_distance |= value; + } else { + val_distance = value << length_param; + + CALLC(value = getbits(reader, length_param)); + val_distance |= value; + } + ++val_distance; + + if (DEBUG_DCL_INFLATE) + fprintf(stderr,"\nCOPY(%d from %d)\n", val_length, val_distance); + + if (val_length + write_pos > length) { + fprintf(stderr, "DCL-INFLATE Error: Write out of bounds while copying %d bytes\n", val_length); + return -SCI_ERROR_DECOMPRESSION_OVERFLOW; + } + + if (write_pos < val_distance) { + fprintf(stderr, "DCL-INFLATE Error: Attempt to copy from before beginning of input stream\n"); + return -SCI_ERROR_DECOMPRESSION_INSANE; + } + + while (val_length) { + int copy_length = (val_length > val_distance)? val_distance : val_length; + + memcpy(dest + write_pos, dest + write_pos - val_distance, copy_length); + + if (DEBUG_DCL_INFLATE) { + int i; + for (i = 0; i < copy_length; i++) + fprintf(stderr,"\33[32;31m%02x\33[37;37m ", dest[write_pos + i]); + fprintf(stderr, "\n"); + } + + val_length -= copy_length; + val_distance += copy_length; + write_pos += copy_length; + } + + } else { /* Copy byte verbatim */ + if (mode == DCL_ASCII_MODE) { + CALLC(value = huffman_lookup(reader, ascii_tree)); + } else { + CALLC(value = getbits(reader, 8)); + } + + dest[write_pos++] = value; + + if (DEBUG_DCL_INFLATE) + fprintf(stderr, "\33[32;31m%02x \33[37;37m", value); + } + } + + return 0; +} + +int +decrypt4(guint8* dest, guint8* src, int length, int complength) +{ + struct bit_read_struct reader; + + reader.length = complength; + reader.bitpos = 0; + reader.bytepos = 0; + reader.data = src; + + return -decrypt4_hdyn(dest, length, &reader); +} + + + + +void decryptinit3(void); +int decrypt3(guint8* dest, guint8* src, int length, int complength); +int decompress1(resource_t *result, int resh, int early); + +int decompress1(resource_t *result, int resh, int sci_version) +{ + guint16 compressedLength; + guint16 compressionMethod, result_size; + guint8 *buffer; + guint8 tempid; + + if (sci_version == SCI_VERSION_1_EARLY) { + if (read(resh, &(result->id),2) != 2) + return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN + result->id = GUINT16_SWAP_LE_BE_CONSTANT(result->id); +#endif + + result->number = result->id & 0x07ff; + result->type = result->id >> 11; + + if ((result->number >= sci_max_resource_nr[SCI_VERSION_1_LATE]) || (result->type > sci_invalid_resource)) + return SCI_ERROR_DECOMPRESSION_INSANE; + } else { + if (read(resh, &tempid, 1) != 1) + return SCI_ERROR_IO_ERROR; + + result->id = tempid; + + result->type = result->id &0x7f; + if (read(resh, &(result->number), 2) != 2) + return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN + result->number = GUINT16_SWAP_LE_BE_CONSTANT(result->number); +#endif /* WORDS_BIGENDIAN */ + if ((result->number >= sci_max_resource_nr[SCI_VERSION_1_LATE]) || (result->type > sci_invalid_resource)) + return SCI_ERROR_DECOMPRESSION_INSANE; + } + + if ((read(resh, &compressedLength, 2) != 2) || + (read(resh, &result_size, 2) != 2) || + (read(resh, &compressionMethod, 2) != 2)) + return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN + compressedLength = GUINT16_SWAP_LE_BE_CONSTANT(compressedLength); + result_size = GUINT16_SWAP_LE_BE_CONSTANT(result_size); + compressionMethod = GUINT16_SWAP_LE_BE_CONSTANT(compressionMethod); +#endif + result->size = result_size; + + if ((result->size > SCI_MAX_RESOURCE_SIZE) || + (compressedLength > SCI_MAX_RESOURCE_SIZE)) + return SCI_ERROR_RESOURCE_TOO_BIG; + + if (compressedLength > 4) + compressedLength -= 4; + else { /* Object has size zero (e.g. view.000 in sq3) (does this really exist?) */ + result->data = 0; + result->status = SCI_STATUS_NOMALLOC; + return SCI_ERROR_EMPTY_OBJECT; + } + + buffer = (guint8*)sci_malloc(compressedLength); + result->data = (unsigned char*)sci_malloc(result->size); + + if (read(resh, buffer, compressedLength) != compressedLength) { + free(result->data); + free(buffer); + return SCI_ERROR_IO_ERROR; + }; + + +#ifdef _SCI_DECOMPRESS_DEBUG + fprintf(stderr, "Resource %i.%s encrypted with method SCI1%c/%hi at %.2f%%" + " ratio\n", + result->number, sci_resource_type_suffixes[result->type], + early? 'e' : 'l', + compressionMethod, + (result->size == 0)? -1.0 : + (100.0 * compressedLength / result->size)); + fprintf(stderr, " compressedLength = 0x%hx, actualLength=0x%hx\n", + compressedLength, result->size); +#endif + + switch(compressionMethod) { + + case 0: /* no compression */ + if (result->size != compressedLength) { + free(result->data); + result->data = NULL; + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_DECOMPRESSION_OVERFLOW; + } + memcpy(result->data, buffer, compressedLength); + result->status = SCI_STATUS_ALLOCATED; + break; + + case 1: /* LZW */ + if (decrypt2(result->data, buffer, result->size, compressedLength)) { + free(result->data); + result->data = 0; /* So that we know that it didn't work */ + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_DECOMPRESSION_OVERFLOW; + } + result->status = SCI_STATUS_ALLOCATED; + break; + + case 2: /* ??? */ + decryptinit3(); + if (decrypt3(result->data, buffer, result->size, compressedLength)) { + free(result->data); + result->data = 0; /* So that we know that it didn't work */ + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_DECOMPRESSION_OVERFLOW; + } + result->status = SCI_STATUS_ALLOCATED; + break; + + case 3: + decryptinit3(); + if (decrypt3(result->data, buffer, result->size, compressedLength)) { + free(result->data); + result->data = 0; /* So that we know that it didn't work */ + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_DECOMPRESSION_OVERFLOW; + } + result->data = view_reorder(result->data, result->size); + result->status = SCI_STATUS_ALLOCATED; + break; + + case 4: + decryptinit3(); + if (decrypt3(result->data, buffer, result->size, compressedLength)) { + free(result->data); + result->data = 0; /* So that we know that it didn't work */ + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_DECOMPRESSION_OVERFLOW; + } + result->data = pic_reorder(result->data, result->size); + result->status = SCI_STATUS_ALLOCATED; + break; + + default: + fprintf(stderr,"Resource %s.%03hi: Compression method SCI1/%hi not " + "supported!\n", sci_resource_types[result->type], result->number, + compressionMethod); + free(result->data); + result->data = 0; /* So that we know that it didn't work */ + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_UNKNOWN_COMPRESSION; + } + + free(buffer); + return 0; +} + diff --git a/engines/sci/scicore/decompress11.c b/engines/sci/scicore/decompress11.c new file mode 100644 index 0000000000..eb3cc39a73 --- /dev/null +++ b/engines/sci/scicore/decompress11.c @@ -0,0 +1,167 @@ +/*************************************************************************** + decompress11.c Copyright (C) 1999 The FreeSCI project + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + +***************************************************************************/ +/* Reads data from a resource file and stores the result in memory */ + +#include +#include + +#define DDEBUG if (0) printf + +void decryptinit3(void); +int decrypt3(guint8* dest, guint8* src, int length, int complength); +int decrypt4(guint8* dest, guint8* src, int length, int complength); + +int decompress11(resource_t *result, int resh, int sci_version) +{ + guint16 compressedLength; + guint16 compressionMethod, result_size; + guint8 *buffer; + guint8 tempid; + + DDEBUG("d1"); + + if (read(resh, &tempid, 1) != 1) + return SCI_ERROR_IO_ERROR; + + result->id = tempid; + + result->type = result->id &0x7f; + if (read(resh, &(result->number), 2) != 2) + return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN + result->number = GUINT16_SWAP_LE_BE_CONSTANT(result->number); +#endif /* WORDS_BIGENDIAN */ + if ((result->type > sci_invalid_resource)) + return SCI_ERROR_DECOMPRESSION_INSANE; + + if ((read(resh, &compressedLength, 2) != 2) || + (read(resh, &result_size, 2) != 2) || + (read(resh, &compressionMethod, 2) != 2)) + return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN + compressedLength = GUINT16_SWAP_LE_BE_CONSTANT(compressedLength); + result_size = GUINT16_SWAP_LE_BE_CONSTANT(result_size); + compressionMethod = GUINT16_SWAP_LE_BE_CONSTANT(compressionMethod); +#endif + result->size = result_size; + + /* if ((result->size < 0) || (compressedLength < 0)) + return SCI_ERROR_DECOMPRESSION_INSANE; */ + /* This return will never happen in SCI0 or SCI1 (does it have any use?) */ + + if ((result->size > SCI_MAX_RESOURCE_SIZE) || + (compressedLength > SCI_MAX_RESOURCE_SIZE)) + return SCI_ERROR_RESOURCE_TOO_BIG; + + if (compressedLength > 0) + compressedLength -= 0; + else { /* Object has size zero (e.g. view.000 in sq3) (does this really exist?) */ + result->data = 0; + result->status = SCI_STATUS_NOMALLOC; + return SCI_ERROR_EMPTY_OBJECT; + } + + buffer = (guint8*)sci_malloc(compressedLength); + result->data = (unsigned char*)sci_malloc(result->size); + + if (read(resh, buffer, compressedLength) != compressedLength) { + free(result->data); + free(buffer); + return SCI_ERROR_IO_ERROR; + }; + + if (!(compressedLength & 1)) { /* Align */ + int foo; + read(resh, &foo, 1); + } + +#ifdef _SCI_DECOMPRESS_DEBUG + fprintf(stderr, "Resource %i.%s encrypted with method SCI1.1/%hi at %.2f%%" + " ratio\n", + result->number, sci_resource_type_suffixes[result->type], + compressionMethod, + (result->size == 0)? -1.0 : + (100.0 * compressedLength / result->size)); + fprintf(stderr, " compressedLength = 0x%hx, actualLength=0x%hx\n", + compressedLength, result->size); +#endif + + DDEBUG("/%d[%d]", compressionMethod, result->size); + + switch(compressionMethod) { + + case 0: /* no compression */ + if (result->size != compressedLength) { + free(result->data); + result->data = NULL; + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_DECOMPRESSION_OVERFLOW; + } + memcpy(result->data, buffer, compressedLength); + result->status = SCI_STATUS_ALLOCATED; + break; + + case 18: + case 19: + case 20: + if (decrypt4(result->data, buffer, result->size, compressedLength)) { + free(result->data); + result->data = 0; /* So that we know that it didn't work */ + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_DECOMPRESSION_OVERFLOW; + } + result->status = SCI_STATUS_ALLOCATED; + break; + + case 3: + case 4: /* NYI */ + fprintf(stderr,"Resource %d.%s: Warning: compression type #%d not yet implemented\n", + result->number, sci_resource_type_suffixes[result->type], compressionMethod); + free(result->data); + result->data = NULL; + result->status = SCI_STATUS_NOMALLOC; + break; + + default: + fprintf(stderr,"Resource %d.%s: Compression method SCI1/%hi not " + "supported!\n", result->number, sci_resource_type_suffixes[result->type], + compressionMethod); + free(result->data); + result->data = NULL; /* So that we know that it didn't work */ + result->status = SCI_STATUS_NOMALLOC; + free(buffer); + return SCI_ERROR_UNKNOWN_COMPRESSION; + } + + free(buffer); + return 0; +} + diff --git a/engines/sci/scicore/exe.c b/engines/sci/scicore/exe.c new file mode 100644 index 0000000000..d2ff72995a --- /dev/null +++ b/engines/sci/scicore/exe.c @@ -0,0 +1,86 @@ +/*************************************************************************** + exe.c Copyright (C) 2005 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +#include + +#include "exe.h" +#include "exe_dec.h" + +extern exe_decompressor_t exe_decompressor_lzexe; +extern exe_decompressor_t exe_decompressor_raw; + +exe_decompressor_t *exe_decompressors[] = { + &exe_decompressor_lzexe, + &exe_decompressor_raw, + NULL +}; + +struct _exe_file +{ + struct _exe_decompressor *decompressor; + struct _exe_handle *handle; +}; + +exe_file_t * +exe_open(const char *filename) +{ + int i = 0; + exe_decompressor_t *dec; + + while ((dec = exe_decompressors[i])) { + exe_handle_t *handle = dec->open(filename); + + if (handle) { + exe_file_t *file = (exe_file_t*)sci_malloc(sizeof(exe_file_t)); + + sciprintf("Scanning '%s' with decompressor '%s'\n", + filename, dec->name); + + file->handle = handle; + file->decompressor = dec; + return file; + } + + i++; + } + + return NULL; +} + +int +exe_read(exe_file_t *file, void *buf, int count) +{ + return file->decompressor->read(file->handle, buf, count); +} + +void +exe_close(exe_file_t *file) +{ + file->decompressor->close(file->handle); + + sci_free(file); +} diff --git a/engines/sci/scicore/exe.h b/engines/sci/scicore/exe.h new file mode 100644 index 0000000000..ebc74ae3bc --- /dev/null +++ b/engines/sci/scicore/exe.h @@ -0,0 +1,60 @@ +/*************************************************************************** + exe.h Copyright (C) 2005 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +#ifndef _SCI_EXE_H_ +#define _SCI_EXE_H_ + +typedef struct _exe_file exe_file_t; + +exe_file_t * +exe_open(const char *filename); +/* Opens an executable file +** Parameters: (const char *) filename: Filename of executable to open +** Returns : (exe_file_t *) File handle, or NULL on error +** This function will try to find a decompressor that can handle this type +** of executable +*/ + +int +exe_read(exe_file_t *file, void *buf, int count); +/* Reads from an executable file +** Parameters: (exe_file_t *) file: File handle +** (void *) buf: Buffer to store decompressed data +** (int) count: Size of decompressed data requested, in bytes +** Returns : (int) Number of bytes of decompressed data that was stored in +** buf. If this value is less than count an error has +** occured, or end-of-file was reached. +*/ + +void +exe_close(exe_file_t *handle); +/* Closes an executable file +** Parameters: (exe_file_t *) file: File handle +** Returns : (void) +*/ + +#endif /* !_SCI_EXE_H_ */ diff --git a/engines/sci/scicore/exe_dec.h b/engines/sci/scicore/exe_dec.h new file mode 100644 index 0000000000..9b30da0772 --- /dev/null +++ b/engines/sci/scicore/exe_dec.h @@ -0,0 +1,66 @@ +/*************************************************************************** + exe_dec.h Copyright (C) 2005 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +#ifndef _SCI_EXE_DEC_H_ +#define _SCI_EXE_DEC_H_ + +typedef struct _exe_handle exe_handle_t; + +typedef struct _exe_decompressor { + const char *name; /* Decompressor name. Unique identifier, should consist + ** of lower-case (where applicable) alphanumerics + */ + + exe_handle_t * (*open) (const char *filename); + /* Opens an executable file + ** Parameters: (const char *) filename: Filename of executable to open. + ** Returns : (exe_handle_t *) Decompressor file handle, or NULL on + ** error. + ** This function will verify that the file can be handled by the + ** decompressor. If this is not the case the function will fail. + */ + + int (*read) (exe_handle_t *handle, void *buf, int count); + /* Reads from executable file + ** Parameters: (exe_handle_t *) handle: Decompressor file handle. + ** (void *) buf: Buffer to store decompressed data. + ** (int) count: Size of decompressed data requested, in + ** bytes. + ** Returns : (int) Number of bytes of decompressed data that was + ** stored in buf. If this value is less than count + ** an error has occured, or end-of-file was + ** reached. + */ + + void (*close) (exe_handle_t *handle); + /* Closes a decompressor file handle. + ** Parameters: (exe_handle_t *) handle: Decompressor file handle. + ** Returns : (void) + */ +} exe_decompressor_t; + +#endif /* !_SCI_EXE_DEC_H_ */ diff --git a/engines/sci/scicore/exe_lzexe.c b/engines/sci/scicore/exe_lzexe.c new file mode 100644 index 0000000000..2331c01fb4 --- /dev/null +++ b/engines/sci/scicore/exe_lzexe.c @@ -0,0 +1,342 @@ +/*************************************************************************** + exe_lzexe.c Copyright (C) 2005 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +/* Based on public domain code by Mitugu Kurizono. */ + +#include +#include +#include "exe_dec.h" + +/* Macro to interpret two sequential bytes as an unsigned integer. */ +#define UINT16(A) ((*((A) + 1) << 8) + *(A)) + +/* The amount of most recent data (in bytes) that we need to keep in the +** buffer. lzexe compression is based on copying chunks of previous data to +** form new data. +*/ +#define LZEXE_WINDOW 8192 + +/* Buffer size. */ +#define LZEXE_BUFFER_SIZE (LZEXE_WINDOW + 4096) + +/* Maximum amount of data (in bytes) that can be in the buffer at the start +** of the decompression loop. The maximum amount of data that can be added +** to the buffer during a single step of the loop is 256 bytes. +*/ +#define LZEXE_BUFFER_MAX (LZEXE_BUFFER_SIZE - 256) + +struct _exe_handle +{ + FILE *f; + + /* Output buffer. */ + guint8 buffer[LZEXE_BUFFER_SIZE]; + guint8 *bufptr; + + /* Bit buffer. Bits [0..count) still contain unprocessed data. */ + int buf; + int count; + + /* End of data flag. */ + int eod; +}; + +static int +lzexe_read_uint16(FILE *f, int *value) +{ + int data; + + if ((*value = fgetc(f)) == EOF) + return 0; + + if ((data = fgetc(f)) == EOF) + return 0; + + *value |= data << 8; + return 1; +} + +static int +lzexe_read_uint8(FILE *f, int *value) +{ + if ((*value = fgetc(f)) == EOF) + return 0; + + return 1; +} + +static int +lzexe_init(exe_handle_t *handle, FILE *f) +{ + handle->f = f; + handle->bufptr = handle->buffer; + handle->eod = 0; + + if (!lzexe_read_uint16(handle->f, &handle->buf)) + return 0; + + handle->count = 16; + return 1; +} + +static int +lzexe_get_bit(exe_handle_t *handle, int *bit) +{ + *bit = handle->buf & 1; + + if (--handle->count == 0) + { + if (!lzexe_read_uint16(handle->f, &handle->buf)) + return 0; + handle->count = 16; + } + else + handle->buf >>= 1; + + return 1; +} + +static int +lzexe_decompress(exe_handle_t *handle) +{ + while (!handle->eod + && handle->bufptr - handle->buffer <= LZEXE_BUFFER_MAX) { + int bit; + int len, span; + + if (!lzexe_get_bit(handle, &bit)) + return 0; + + if (bit) { + /* 1: copy byte verbatim. */ + + int data; + + if (!lzexe_read_uint8(handle->f, &data)) + return 0; + + *handle->bufptr++ = data; + + continue; + } + + if (!lzexe_get_bit(handle, &bit)) + return 0; + + if (!bit) { + /* 00: copy small block. */ + + /* Next two bits indicate block length - 2. */ + if (!lzexe_get_bit(handle, &bit)) + return 0; + + len = bit << 1; + + if (!lzexe_get_bit(handle, &bit)) + return 0; + + len |= bit; + len += 2; + + /* Read span byte. This forms the low byte of a + ** negative two's compliment value. + */ + if (!lzexe_read_uint8(handle->f, &span)) + return 0; + + /* Convert to negative integer. */ + span -= 256; + } else { + /* 01: copy large block. */ + int data; + + /* Read low byte of span value. */ + if (!lzexe_read_uint8(handle->f, &span)) + return 0; + + /* Read next byte. Bits [7..3] contain bits [12..8] + ** of span value. Bits [2..0] contain block length - + ** 2. + */ + if (!lzexe_read_uint8(handle->f, &data)) + return 0; + span |= (data & 0xf8) << 5; + /* Convert to negative integer. */ + span -= 8192; + + len = (data & 7) + 2; + + if (len == 2) { + /* Next byte is block length value - 1. */ + if (!lzexe_read_uint8(handle->f, &len)) + return 0; + + if (len == 0) { + /* End of data reached. */ + handle->eod = 1; + break; + } + + if (len == 1) + /* Segment change marker. */ + continue; + + len++; + } + } + + assert(handle->bufptr + span >= handle->buffer); + + /* Copy block. */ + while (len-- > 0) { + *handle->bufptr = *(handle->bufptr + span); + handle->bufptr++; + } + } + + return 1; +} + +static exe_handle_t * +lzexe_open(const char *filename) +{ + exe_handle_t *handle; + guint8 head[0x20]; + guint8 size[2]; + off_t fpos; + + FILE *f = sci_fopen(filename, "rb"); + + if (!f) + return NULL; + + /* Read exe header plus possible lzexe signature. */ + if (fread(head, 1, 0x20, f) != 0x20) + return NULL; + + /* Verify "MZ" signature, header size == 2 paragraphs and number of + ** overlays == 0. + */ + if (UINT16(head) != 0x5a4d || UINT16(head + 8) != 2 + || UINT16(head + 0x1a) != 0) + return NULL; + + /* Verify that first relocation item offset is 0x1c. */ + if (UINT16(head + 0x18) != 0x1c) + return NULL; + + /* Look for lzexe signature. */ + if (memcmp(head + 0x1c, "LZ09", 4) + && memcmp(head + 0x1c, "LZ91", 4)) { + return NULL; + } + + /* Calculate code segment offset in exe file. */ + fpos = (UINT16(head + 0x16) + UINT16(head + 8)) << 4; + /* Seek to offset 8 of info table at start of code segment. */ + if (fseek(f, fpos + 8, SEEK_SET) == -1) + return NULL; + + /* Read size of compressed data in paragraphs. */ + if (fread(size, 1, 2, f) != 2) + return NULL; + + /* Move file pointer to start of compressed data. */ + fpos -= UINT16(size) << 4; + if (fseek(f, fpos, SEEK_SET) == -1) + return NULL; + + handle = (exe_handle_t*)sci_malloc(sizeof(exe_handle_t)); + + if (!lzexe_init(handle, f)) { + sci_free(handle); + return NULL; + } + + return handle; +} + +static int +lzexe_read(exe_handle_t *handle, void *buf, int count) +{ + int done = 0; + + while (done != count) { + int size, copy, i; + int left = count - done; + + if (!lzexe_decompress(handle)) + return done; + + /* Total amount of bytes in buffer. */ + size = handle->bufptr - handle->buffer; + + /* If we're not at end of data we need to maintain the + ** window. + */ + if (!handle->eod) + copy = size - LZEXE_WINDOW; + else { + if (size == 0) + /* No data left. */ + return done; + + copy = size; + } + + /* Do not copy more than requested. */ + if (copy > left) + copy = left; + + memcpy((char *) buf + done, handle->buffer, copy); + + /* Move remaining data to start of buffer. */ + for (i = copy; i < size; i++) + handle->buffer[i - copy] = handle->buffer[i]; + + handle->bufptr -= copy; + done += copy; + } + + return done; +} + +static void +lzexe_close(exe_handle_t *handle) +{ + fclose(handle->f); + + sci_free(handle); +} + +exe_decompressor_t +exe_decompressor_lzexe = { + "lzexe", + lzexe_open, + lzexe_read, + lzexe_close +}; diff --git a/engines/sci/scicore/exe_raw.c b/engines/sci/scicore/exe_raw.c new file mode 100644 index 0000000000..ecc7089b2a --- /dev/null +++ b/engines/sci/scicore/exe_raw.c @@ -0,0 +1,73 @@ +/*************************************************************************** + exe_raw.c Copyright (C) 2005 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +#include +#include + +struct _exe_handle +{ + FILE *f; +}; + +#include "exe_dec.h" + +static exe_handle_t * +raw_open(const char *filename) +{ + FILE *f = sci_fopen(filename, "rb"); + exe_handle_t *handle; + + if (!f) + return NULL; + + handle = (exe_handle_t*)sci_malloc(sizeof(exe_handle_t)); + handle->f = f; + + return handle; +} + +static int +raw_read(exe_handle_t *handle, void *buf, int count) +{ + return fread(buf, 1, count, handle->f); +} + +static void +raw_close(exe_handle_t *handle) +{ + fclose(handle->f); + + sci_free(handle); +} + +exe_decompressor_t +exe_decompressor_raw = { + "raw", + raw_open, + raw_read, + raw_close +}; diff --git a/engines/sci/scicore/fnmatch.c b/engines/sci/scicore/fnmatch.c new file mode 100644 index 0000000000..ffc9a1b451 --- /dev/null +++ b/engines/sci/scicore/fnmatch.c @@ -0,0 +1,849 @@ +/* fnmatch.c -- ksh-like extended pattern matching for the shell and filename + globbing. */ + +/* Copyright (C) 1991, 1997 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2, or (at your option) any later + version. + + Bash is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with Bash; see the file COPYING. If not, write to the Free Software + Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifndef HAVE_FNMATCH + +#include /* for debugging */ + +#include +#include +#include + +#if defined (HAVE_STRING_H) +# include +#else +# include +#endif /* HAVE_STRING_H */ + +static int gmatch (); +static char *brackmatch (); +#ifdef EXTENDED_GLOB +static int extmatch (); +static char *patscan (); +#endif + +#if !defined (isascii) +# define isascii(c) ((unsigned int)(c) <= 0177) +#endif + +/* Note that these evaluate C many times. */ + +#ifndef isblank +# define isblank(c) ((c) == ' ' || (c) == '\t') +#endif + +#ifndef isgraph +# define isgraph(c) ((c) != ' ' && isprint((c))) +#endif + +#ifndef isxdigit +# define isxdigit(c) (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F')) +#endif + +/* The result of FOLD is an `unsigned char' */ +# define FOLD(c) ((flags & FNM_CASEFOLD) && isupper ((unsigned char)c) \ + ? tolower ((unsigned char)c) \ + : ((unsigned char)c)) + +#ifndef STREQ +#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0) +#define STREQN(a, b, n) ((a)[0] == (b)[0] && strncmp(a, b, n) == 0) +#endif + +/* We use strcoll(3) for range comparisons in bracket expressions, + even though it can have unwanted side effects in locales + other than POSIX or US. For instance, in the de locale, [A-Z] matches + all characters. */ + +#if defined (HAVE_STRCOLL) +/* Helper function for collating symbol equivalence. */ +static int rangecmp (c1, c2) + int c1, c2; +{ + static char s1[2] = { ' ', '\0' }; + static char s2[2] = { ' ', '\0' }; + int ret; + + /* Eight bits only. Period. */ + c1 &= 0xFF; + c2 &= 0xFF; + + if (c1 == c2) + return (0); + + s1[0] = c1; + s2[0] = c2; + + if ((ret = strcoll (s1, s2)) != 0) + return ret; + return (c1 - c2); +} +#else /* !HAVE_STRCOLL */ +# define rangecmp(c1, c2) ((int)(c1) - (int)(c2)) +#endif /* !HAVE_STRCOLL */ + +#if defined (HAVE_STRCOLL) +static int collequiv (c1, c2) + int c1, c2; +{ + return (rangecmp (c1, c2) == 0); +} +#else +# define collequiv(c1, c2) ((c1) == (c2)) +#endif + +static int +collsym (s, len) + char *s; + int len; +{ + register struct _collsym *csp; + + for (csp = posix_collsyms; csp->name; csp++) + { + if (STREQN(csp->name, s, len) && csp->name[len] == '\0') + return (csp->code); + } + if (len == 1) + return s[0]; + return -1; +} + +int +fnmatch (pattern, string, flags) + char *pattern; + char *string; + int flags; +{ + char *se, *pe; + + if (string == 0 || pattern == 0) + return FNM_NOMATCH; + + se = string + strlen (string); + pe = pattern + strlen (pattern); + + return (gmatch (string, se, pattern, pe, flags)); +} + +/* Match STRING against the filename pattern PATTERN, returning zero if + it matches, FNM_NOMATCH if not. */ +static int +gmatch (string, se, pattern, pe, flags) + char *string, *se; + char *pattern, *pe; + int flags; +{ + register char *p, *n; /* pattern, string */ + register char c; /* current pattern character */ + register char sc; /* current string character */ + + p = pattern; + n = string; + + if (string == 0 || pattern == 0) + return FNM_NOMATCH; + +#if DEBUG_MATCHING +fprintf(stderr, "gmatch: string = %s; se = %s\n", string, se); +fprintf(stderr, "gmatch: pattern = %s; pe = %s\n", pattern, pe); +#endif + + while (p < pe) + { + c = *p++; + c = FOLD (c); + + sc = n < se ? *n : '\0'; + +#ifdef EXTENDED_GLOB + /* extmatch () will handle recursively calling gmatch, so we can + just return what extmatch() returns. */ + if ((flags & FNM_EXTMATCH) && *p == '(' && + (c == '+' || c == '*' || c == '?' || c == '@' || c == '!')) /* ) */ + { + int lflags; + /* If we're not matching the start of the string, we're not + concerned about the special cases for matching `.' */ + lflags = (n == string) ? flags : (flags & ~FNM_PERIOD); + return (extmatch (c, n, se, p, pe, lflags)); + } +#endif + + switch (c) + { + case '?': /* Match single character */ + if (sc == '\0') + return FNM_NOMATCH; + else if ((flags & FNM_PATHNAME) && sc == '/') + /* If we are matching a pathname, `?' can never match a `/'. */ + return FNM_NOMATCH; + else if ((flags & FNM_PERIOD) && sc == '.' && + (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/'))) + /* `?' cannot match a `.' if it is the first character of the + string or if it is the first character following a slash and + we are matching a pathname. */ + return FNM_NOMATCH; + break; + + case '\\': /* backslash escape removes special meaning */ + if (p == pe) + return FNM_NOMATCH; + + if ((flags & FNM_NOESCAPE) == 0) + { + c = *p++; + /* A trailing `\' cannot match. */ + if (p > pe) + return FNM_NOMATCH; + c = FOLD (c); + } + if (FOLD (sc) != (unsigned char)c) + return FNM_NOMATCH; + break; + + case '*': /* Match zero or more characters */ + if (p == pe) + return 0; + + if ((flags & FNM_PERIOD) && sc == '.' && + (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/'))) + /* `*' cannot match a `.' if it is the first character of the + string or if it is the first character following a slash and + we are matching a pathname. */ + return FNM_NOMATCH; + + /* Collapse multiple consecutive, `*' and `?', but make sure that + one character of the string is consumed for each `?'. */ + for (c = *p++; (c == '?' || c == '*'); c = *p++) + { + if ((flags & FNM_PATHNAME) && sc == '/') + /* A slash does not match a wildcard under FNM_PATHNAME. */ + return FNM_NOMATCH; + else if (c == '?') + { + if (sc == '\0') + return FNM_NOMATCH; + /* One character of the string is consumed in matching + this ? wildcard, so *??? won't match if there are + fewer than three characters. */ + n++; + sc = n < se ? *n : '\0'; + } + +#ifdef EXTENDED_GLOB + /* Handle ******(patlist) */ + if ((flags & FNM_EXTMATCH) && c == '*' && *p == '(') /*)*/ + { + char *newn; + /* We need to check whether or not the extended glob + pattern matches the remainder of the string. + If it does, we match the entire pattern. */ + for (newn = n; newn < se; ++newn) + { + if (extmatch (c, newn, se, p, pe, flags) == 0) + return (0); + } + /* We didn't match the extended glob pattern, but + that's OK, since we can match 0 or more occurrences. + We need to skip the glob pattern and see if we + match the rest of the string. */ + newn = patscan (p + 1, pe, 0); + p = newn; + } +#endif + if (p == pe) + break; + } + + /* If we've hit the end of the pattern and the last character of + the pattern was handled by the loop above, we've succeeded. + Otherwise, we need to match that last character. */ + if (p == pe && (c == '?' || c == '*')) + return (0); + + /* General case, use recursion. */ + { + unsigned char c1; + + c1 = (unsigned char)((flags & FNM_NOESCAPE) == 0 && c == '\\') ? *p : c; + c1 = FOLD (c1); + for (--p; n < se; ++n) + { + /* Only call fnmatch if the first character indicates a + possible match. We can check the first character if + we're not doing an extended glob match. */ + if ((flags & FNM_EXTMATCH) == 0 && c != '[' && FOLD (*n) != c1) /*]*/ + continue; + + /* If we're doing an extended glob match and the pattern is not + one of the extended glob patterns, we can check the first + character. */ + if ((flags & FNM_EXTMATCH) && p[1] != '(' && /*)*/ + strchr ("?*+@!", *p) == 0 && c != '[' && FOLD (*n) != c1) /*]*/ + continue; + + /* Otherwise, we just recurse. */ + if (gmatch (n, se, p, pe, flags & ~FNM_PERIOD) == 0) + return (0); + } + return FNM_NOMATCH; + } + + case '[': + { + if (sc == '\0' || n == se) + return FNM_NOMATCH; + + /* A character class cannot match a `.' if it is the first + character of the string or if it is the first character + following a slash and we are matching a pathname. */ + if ((flags & FNM_PERIOD) && sc == '.' && + (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/'))) + return (FNM_NOMATCH); + + p = brackmatch (p, sc, flags); + if (p == 0) + return FNM_NOMATCH; + } + break; + + default: + if ((unsigned char)c != FOLD (sc)) + return (FNM_NOMATCH); + } + + ++n; + } + + if (n == se) + return (0); + + if ((flags & FNM_LEADING_DIR) && *n == '/') + /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */ + return 0; + + return (FNM_NOMATCH); +} + +/* Parse a bracket expression collating symbol ([.sym.]) starting at P, find + the value of the symbol, and move P past the collating symbol expression. + The value is returned in *VP, if VP is not null. */ +static char * +parse_collsym (p, vp) + char *p; + int *vp; +{ + register int pc; + int val; + + p++; /* move past the `.' */ + + for (pc = 0; p[pc]; pc++) + if (p[pc] == '.' && p[pc+1] == ']') + break; + val = collsym (p, pc); + if (vp) + *vp = val; + return (p + pc + 2); +} + +static char * +brackmatch (p, test, flags) + char *p; + unsigned char test; + int flags; +{ + register char cstart, cend, c; + register int not; /* Nonzero if the sense of the character class is inverted. */ + int pc, brcnt; + char *savep; + + test = FOLD (test); + + savep = p; + + /* POSIX.2 3.13.1 says that an exclamation mark (`!') shall replace the + circumflex (`^') in its role in a `nonmatching list'. A bracket + expression starting with an unquoted circumflex character produces + unspecified results. This implementation treats the two identically. */ + if (not = (*p == '!' || *p == '^')) + ++p; + + c = *p++; + for (;;) + { + /* Initialize cstart and cend in case `-' is the last + character of the pattern. */ + cstart = cend = c; + + /* POSIX.2 equivalence class: [=c=]. See POSIX.2 2.8.3.2. Find + the end of the equivalence class, move the pattern pointer past + it, and check for equivalence. XXX - this handles only + single-character equivalence classes, which is wrong, or at + least incomplete. */ + if (c == '[' && *p == '=' && p[2] == '=' && p[3] == ']') + { + pc = FOLD (p[1]); + p += 4; + if (collequiv (test, pc)) + { +/*[*/ /* Move past the closing `]', since the first thing we do at + the `matched:' label is back p up one. */ + p++; + goto matched; + } + else + { + c = *p++; + if (c == '\0') + return ((test == '[') ? savep : (char *)0); /*]*/ + c = FOLD (c); + continue; + } + } + + /* POSIX.2 character class expression. See POSIX.2 2.8.3.2. */ + if (c == '[' && *p == ':') /*]*/ + { + pc = 0; /* make sure invalid char classes don't match. */ + if (STREQN (p+1, "alnum:]", 7)) + { pc = isalnum (test); p += 8; } + else if (STREQN (p+1, "alpha:]", 7)) + { pc = isalpha (test); p += 8; } + else if (STREQN (p+1, "blank:]", 7)) + { pc = isblank (test); p += 8; } + else if (STREQN (p+1, "cntrl:]", 7)) + { pc = iscntrl (test); p += 8; } + else if (STREQN (p+1, "digit:]", 7)) + { pc = isdigit (test); p += 8; } + else if (STREQN (p+1, "graph:]", 7)) + { pc = isgraph (test); p += 8; } + else if (STREQN (p+1, "lower:]", 7)) + { pc = islower (test); p += 8; } + else if (STREQN (p+1, "print:]", 7)) + { pc = isprint (test); p += 8; } + else if (STREQN (p+1, "punct:]", 7)) + { pc = ispunct (test); p += 8; } + else if (STREQN (p+1, "space:]", 7)) + { pc = isspace (test); p += 8; } + else if (STREQN (p+1, "upper:]", 7)) + { pc = isupper (test); p += 8; } + else if (STREQN (p+1, "xdigit:]", 8)) + { pc = isxdigit (test); p += 9; } + else if (STREQN (p+1, "ascii:]", 7)) + { pc = isascii (test); p += 8; } + if (pc) + { +/*[*/ /* Move past the closing `]', since the first thing we do at + the `matched:' label is back p up one. */ + p++; + goto matched; + } + else + { + /* continue the loop here, since this expression can't be + the first part of a range expression. */ + c = *p++; + if (c == '\0') + return ((test == '[') ? savep : (char *)0); + else if (c == ']') + break; + c = FOLD (c); + continue; + } + } + + /* POSIX.2 collating symbols. See POSIX.2 2.8.3.2. Find the end of + the symbol name, make sure it is terminated by `.]', translate + the name to a character using the external table, and do the + comparison. */ + if (c == '[' && *p == '.') + { + p = parse_collsym (p, &pc); + /* An invalid collating symbol cannot be the first point of a + range. If it is, we set cstart to one greater than `test', + so any comparisons later will fail. */ + cstart = (pc == -1) ? test + 1 : pc; + } + + if (!(flags & FNM_NOESCAPE) && c == '\\') + { + if (*p == '\0') + return (char *)0; + cstart = cend = *p++; + } + + cstart = cend = FOLD (cstart); + + /* POSIX.2 2.8.3.1.2 says: `An expression containing a `[' that + is not preceded by a backslash and is not part of a bracket + expression produces undefined results.' This implementation + treats the `[' as just a character to be matched if there is + not a closing `]'. */ + if (c == '\0') + return ((test == '[') ? savep : (char *)0); + + c = *p++; + c = FOLD (c); + + if ((flags & FNM_PATHNAME) && c == '/') + /* [/] can never match when matching a pathname. */ + return (char *)0; + + /* This introduces a range, unless the `-' is the last + character of the class. Find the end of the range + and move past it. */ + if (c == '-' && *p != ']') + { + cend = *p++; + if (!(flags & FNM_NOESCAPE) && cend == '\\') + cend = *p++; + if (cend == '\0') + return (char *)0; + if (cend == '[' && *p == '.') + { + p = parse_collsym (p, &pc); + /* An invalid collating symbol cannot be the second part of a + range expression. If we get one, we set cend to one fewer + than the test character to make sure the range test fails. */ + cend = (pc == -1) ? test - 1 : pc; + } + cend = FOLD (cend); + + c = *p++; + + /* POSIX.2 2.8.3.2: ``The ending range point shall collate + equal to or higher than the starting range point; otherwise + the expression shall be treated as invalid.'' Note that this + applies to only the range expression; the rest of the bracket + expression is still checked for matches. */ + if (rangecmp (cstart, cend) > 0) + { + if (c == ']') + break; + c = FOLD (c); + continue; + } + } + + if (rangecmp (test, cstart) >= 0 && rangecmp (test, cend) <= 0) + goto matched; + + if (c == ']') + break; + } + /* No match. */ + return (!not ? (char *)0 : p); + +matched: + /* Skip the rest of the [...] that already matched. */ +#if 0 + brcnt = (c != ']') + (c == '[' && (*p == '=' || *p == ':' || *p == '.')); +#else + c = *--p; + brcnt = 1; +#endif + while (brcnt > 0) + { + /* A `[' without a matching `]' is just another character to match. */ + if (c == '\0') + return ((test == '[') ? savep : (char *)0); + + c = *p++; + if (c == '[' && (*p == '=' || *p == ':' || *p == '.')) + brcnt++; + else if (c == ']') + brcnt--; + else if (!(flags & FNM_NOESCAPE) && c == '\\') + { + if (*p == '\0') + return (char *)0; + /* XXX 1003.2d11 is unclear if this is right. */ + ++p; + } + } + return (not ? (char *)0 : p); +} + +#if defined (EXTENDED_GLOB) +/* ksh-like extended pattern matching: + + [?*+@!](pat-list) + + where pat-list is a list of one or patterns separated by `|'. Operation + is as follows: + + ?(patlist) match zero or one of the given patterns + *(patlist) match zero or more of the given patterns + +(patlist) match one or more of the given patterns + @(patlist) match exactly one of the given patterns + !(patlist) match anything except one of the given patterns +*/ + +/* Scan a pattern starting at STRING and ending at END, keeping track of + embedded () and []. If DELIM is 0, we scan until a matching `)' + because we're scanning a `patlist'. Otherwise, we scan until we see + DELIM. In all cases, we never scan past END. The return value is the + first character after the matching DELIM. */ +static char * +patscan (string, end, delim) + char *string, *end; + int delim; +{ + int pnest, bnest, cchar; + char *s, c, *bfirst; + + pnest = bnest = cchar = 0; + bfirst = 0; + for (s = string; c = *s; s++) + { + if (s >= end) + return (s); + switch (c) + { + case '\0': + return ((char *)0); + + /* `[' is not special inside a bracket expression, but it may + introduce one of the special POSIX bracket expressions + ([.SYM.], [=c=], [: ... :]) that needs special handling. */ + case '[': + if (bnest == 0) + { + bfirst = s + 1; + if (*bfirst == '!' || *bfirst == '^') + bfirst++; + bnest++; + } + else if (s[1] == ':' || s[1] == '.' || s[1] == '=') + cchar = s[1]; + break; + + /* `]' is not special if it's the first char (after a leading `!' + or `^') in a bracket expression or if it's part of one of the + special POSIX bracket expressions ([.SYM.], [=c=], [: ... :]) */ + case ']': + if (bnest) + { + if (cchar && s[-1] == cchar) + cchar = 0; + else if (s != bfirst) + { + bnest--; + bfirst = 0; + } + } + break; + + case '(': + if (bnest == 0) + pnest++; + break; + + case ')': +#if 0 + if (bnest == 0) + pnest--; + if (pnest <= 0) + return ++s; +#else + if (bnest == 0 && pnest-- <= 0) + return ++s; +#endif + break; + + case '|': + if (bnest == 0 && pnest == 0 && delim == '|') + return ++s; + break; + } + } + + return (char *)0; +} + +/* Return 0 if dequoted pattern matches S in the current locale. */ +static int +strcompare (p, pe, s, se) + char *p, *pe, *s, *se; +{ + int ret; + char c1, c2; + + c1 = *pe; + c2 = *se; + + *pe = *se = '\0'; +#if defined (HAVE_STRCOLL) + ret = strcoll (p, s); +#else + ret = strcmp (p, s); +#endif + + *pe = c1; + *se = c2; + + return (ret == 0 ? ret : FNM_NOMATCH); +} + +/* Match a ksh extended pattern specifier. Return FNM_NOMATCH on failure or + 0 on success. This is handed the entire rest of the pattern and string + the first time an extended pattern specifier is encountered, so it calls + gmatch recursively. */ +static int +extmatch (xc, s, se, p, pe, flags) + int xc; /* select which operation */ + char *s, *se; + char *p, *pe; + int flags; +{ + char *prest; /* pointer to rest of pattern */ + char *psub; /* pointer to sub-pattern */ + char *pnext; /* pointer to next sub-pattern */ + char *srest; /* pointer to rest of string */ + int m1, m2; + +#if DEBUG_MATCHING +fprintf(stderr, "extmatch: xc = %c\n", xc); +fprintf(stderr, "extmatch: s = %s; se = %s\n", s, se); +fprintf(stderr, "extmatch: p = %s; pe = %s\n", p, pe); +#endif + + prest = patscan (p + (*p == '('), pe, 0); /* ) */ + if (prest == 0) + /* If PREST is 0, we failed to scan a valid pattern. In this + case, we just want to compare the two as strings. */ + return (strcompare (p - 1, pe, s, se)); + + switch (xc) + { + case '+': /* match one or more occurrences */ + case '*': /* match zero or more occurrences */ + /* If we can get away with no matches, don't even bother. Just + call gmatch on the rest of the pattern and return success if + it succeeds. */ + if (xc == '*' && (gmatch (s, se, prest, pe, flags) == 0)) + return 0; + + /* OK, we have to do this the hard way. First, we make sure one of + the subpatterns matches, then we try to match the rest of the + string. */ + for (psub = p + 1; ; psub = pnext) + { + pnext = patscan (psub, pe, '|'); + for (srest = s; srest <= se; srest++) + { + /* Match this substring (S -> SREST) against this + subpattern (psub -> pnext - 1) */ + m1 = gmatch (s, srest, psub, pnext - 1, flags) == 0; + /* OK, we matched a subpattern, so make sure the rest of the + string matches the rest of the pattern. Also handle + multiple matches of the pattern. */ + if (m1) + m2 = (gmatch (srest, se, prest, pe, flags) == 0) || + (s != srest && gmatch (srest, se, p - 1, pe, flags) == 0); + if (m1 && m2) + return (0); + } + if (pnext == prest) + break; + } + return (FNM_NOMATCH); + + case '?': /* match zero or one of the patterns */ + case '@': /* match exactly one of the patterns */ + /* If we can get away with no matches, don't even bother. Just + call gmatch on the rest of the pattern and return success if + it succeeds. */ + if (xc == '?' && (gmatch (s, se, prest, pe, flags) == 0)) + return 0; + + /* OK, we have to do this the hard way. First, we see if one of + the subpatterns matches, then, if it does, we try to match the + rest of the string. */ + for (psub = p + 1; ; psub = pnext) + { + pnext = patscan (psub, pe, '|'); + srest = (prest == pe) ? se : s; + for ( ; srest <= se; srest++) + { + if (gmatch (s, srest, psub, pnext - 1, flags) == 0 && + gmatch (srest, se, prest, pe, flags) == 0) + return (0); + } + if (pnext == prest) + break; + } + return (FNM_NOMATCH); + + case '!': /* match anything *except* one of the patterns */ + for (srest = s; srest <= se; srest++) + { + m1 = 0; + for (psub = p + 1; ; psub = pnext) + { + pnext = patscan (psub, pe, '|'); + /* If one of the patterns matches, just bail immediately. */ + if (m1 = (gmatch (s, srest, psub, pnext - 1, flags) == 0)) + break; + if (pnext == prest) + break; + } + if (m1 == 0 && gmatch (srest, se, prest, pe, flags) == 0) + return (0); + } + return (FNM_NOMATCH); + } + + return (FNM_NOMATCH); +} +#endif /* EXTENDED_GLOB */ + +#ifdef TEST +main (c, v) + int c; + char **v; +{ + char *string, *pat; + + string = v[1]; + pat = v[2]; + + if (fnmatch (pat, string, 0) == 0) + { + printf ("%s matches %s\n", string, pat); + exit (0); + } + else + { + printf ("%s does not match %s\n", string, pat); + exit (1); + } +} +#endif + +#endif + diff --git a/engines/sci/scicore/games.h b/engines/sci/scicore/games.h new file mode 100644 index 0000000000..d8b9642fba --- /dev/null +++ b/engines/sci/scicore/games.h @@ -0,0 +1,125 @@ +/*************************************************************************** + games.h Copyright (C) 2002 Solomon Peachy + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Solomon Peachy [pizza@shaftnet.org] + +***************************************************************************/ + +/* Game identification */ + +#ifndef _SCI_GAMES_H_ +#define _SCI_GAMES_H_ + +#ifndef NEED_SCI_VERSIONS +# error "You shouldn't be including this header file." +#endif + +#include + +typedef struct _sci_game { + int id; /* currently CRC of resource.001 */ + int res_version; + sci_version_t version; + const char *name; +} sci_game_t; + +/* Interpreter versions for Amiga and Atari ST ports are tentative */ +sci_game_t sci_games[] = { + { 0x5D451535, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,510), "Leisure Suit Larry 1 v1.0-mac"}, /* x.yyy.zzz */ /* Launcher says v2.0, game crashes on DoAvoider */ + { 0x6C176EE0, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,577), "Leisure Suit Larry 1 v2.1"}, + { 0x1C36E076, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,510), "Leisure Suit Larry 1 v1.000-es"}, /* 1.SQ4.057 */ /* Crashes on function 0x7b */ + + { 0xFEAB629D, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,343), "Leisure Suit Larry 2 v1.000.011-3.5"}, + { 0x13DD3CD2, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,343), "Leisure Suit Larry 2 v1.000.011-5.25" }, + { 0x1D0F3B31, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Leisure Suit Larry 2 v1.001.006-st"}, /* 1.000.159 */ + { 0x40BEC726, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,409), "Leisure Suit Larry 2 v1.002.000-3.5"}, + { 0x0C848403, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,409), "Leisure Suit Larry 2 v1.002.000-5.25" }, + { 0x7E9CF339, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Leisure Suit Larry 2 v1.003-ami"}, /* x.yyy.zzz */ + + { 0x06D737B5, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Leisure Suit Larry 3 v1.003-3.5" }, + { 0xE0A1C352, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Leisure Suit Larry 3 v1.003-5.25" }, + { 0xC48FE83A, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Leisure Suit Larry 3 v1.021-3.5" }, + { 0x484587DD, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Leisure Suit Larry 3 v1.021-5.25"}, +/* { 0x????????, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Leisure Suit Larry 3 v1.021-st"},*/ /* 1.002.026 */ + { 0x6348030A, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Leisure Suit Larry 3 v1.039-ami"}, /* 1.002.032 */ + + { 0x94EA377B, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,685), "CB1" }, + { 0xFD9EE7BD, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,685), "Camelot" }, + { 0x2829987F, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,685), "Camelot" }, + { 0x980CEAD3, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,629), "Demo Quest" }, + { 0x3DB972CA, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Hoyle 2" }, + { 0xC0B37651, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,685), "Iceman" }, + { 0xDABA6B8A, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,999), "KQ1 v1.000.051-3.5" }, /* S.old.010 */ + { 0x270E37F3, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,274), "KQ4" }, + { 0x685F1205, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,502), "KQ4" }, + { 0xC14E3A2A, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,395), "PQ2" }, + { 0x4BD66036, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,490), "PQ2" }, + { 0x7132D6D8, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,629), "QfG1" }, + { 0xF8F4913F, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,685), "SQ3" }, + { 0x34FBC324, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,999), "SQ3/DE" }, /* S.old.114 */ + { 0xE4A3234D, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,506), "Fun Seekers Guide v1.02"}, + { 0x85AFE241, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,519), "Hoyle 1 v1.000.104"}, + { 0xE0E070C3, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Hoyle 2 v1.000.011"}, + { 0xD0B8794E, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,668), "Iceman v1.023"}, + { 0x94EA377B, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,631), "The Colonel's Bequest v1.000.046"}, + { 0x28543FDF, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,453), "Astro Chicken"}, + { 0x31F46F7D, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,453), "Space Quest III v1.0V int"}, + { 0xAA2C94B9, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,685), "Mixed-Up Mother Goose v1.011 Int.#8.2.90"}, + { 0x3B15678B, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,631), "The Colonel's Bequest v1.000.046-3.5"}, + { 0x0E042F46, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,530), "Hoyle 1 v1.000.113-3.5"}, + { 0x1EACB959, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,566), "HQ v1.000-5.25"}, + { 0x2BEAF5E7, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,566), "HQ v1.001-5.25"}, + { 0x63626D3E, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,668), "Iceman v1.023-5.25"}, + { 0xDA5E7B7D, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,409), "KQ4 v1.003.006-3.5"}, + { 0x376A5472, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,502), "KQ4 v1.006.003-5.25"}, + { 0x364B40B2, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,395), "PQ2 v1.001.000-5.25"}, + { 0x664B4123, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,409), "PQ2 v1.001.006-3.5"}, + { 0x379F4582, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,453), "SQ3 v1.0V-5.25"}, + { 0x04B0B081, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,294), "xmascard v1.04"}, + + { 0x4447B28D, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,72), "Trial v1.105"}, + + { 0xB1C2CCAE, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,200), "SQ4 v1.052"}, /* 1.000.753 */ + { 0xAA6AF6A9, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,60), "KQ5 v0.000.062"}, + { 0x092C2C0D, 3, SCI_VERSION(1,000,172), "jones v1.000.060"}, /* x.yyy.zzz */ + + { 0xC415A485, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,172), "jones v1.000.060-cd"}, /* x.yyy.zzz */ + + { 0x89C595E3, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,510), "SQ1 v2.000"}, /* T.A00.081 */ + { 0x09D4FC54, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,510), "LSL5 v1.000"}, /* T.A00.169 */ + { 0xF3ED1D81, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,510), "PQ3 v1.00"}, /* T.A00.178 */ + { 0x501B5E6B, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,510), "Brain v1.000"}, /* 1.000.044 */ + { 0xB1B7279B, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,510), "Longbow v1.000"}, /* 1.000.168 */ + + { 0x82595EBE, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,453), "SQ3 v1.0V-ami"}, /* x.yyy.zzz */ + { 0xF6080B61, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,530), "Hoyle 1 v1.000.139-ami"}, /* x.yyy.zzz */ + + { 0x8AFEA2D0, 2, SCI_VERSION(1,000,000), "KQ1 v1.000.054-ami"}, /* 1.003.007 */ + + /* Undetermined Amiga versions: */ +/* { 0x8AE5F854, ?, SCI_VERSION(), "ARTHUR" }, */ +/* { 0x9FB7015B, ?, SCI_VERSION(), "CB1" }, */ +/* { 0x560CEDD5, ?, SCI_VERSION(), "iceMan" }, */ + + { 0, 0, 0, NULL } /* terminator */ +}; + +#endif /* _SCI_GAMES_H_ */ diff --git a/engines/sci/scicore/hashmap.c b/engines/sci/scicore/hashmap.c new file mode 100644 index 0000000000..1b2b6af5c1 --- /dev/null +++ b/engines/sci/scicore/hashmap.c @@ -0,0 +1,186 @@ +/*************************************************************************** + hashmap.c Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Defines a universal hash map. +** Preprocessor parameters: +** TYPE: The type to hash +** HASH_MAX: Maximum hash value +** HASH(x): Hashes a value of type TYPE to an int from 0 to HASH_MAX +*/ +#include +#include + +#ifdef MUST_FREE +# define CLEAR_NODE(n) free(n->name); n->name = NULL +# define FREE_PARAM(n) free(n) +#else +# define CLEAR_NODE(n) +# define FREE_PARAM(n) +#endif + + +#ifdef DUPLICATOR +# define DUP_VALUE(x) DUPLICATOR((x)) +#else +# define DUP_VALUE(x) (x) +#endif + + +#define DEFINE_FUNCTIONS(TYPE) \ + \ + \ +TYPE##_hash_map_t * \ +new_##TYPE##_hash_map(void) \ +{ \ + TYPE##_hash_map_t *map = (TYPE##_hash_map_t*)calloc(1, sizeof(TYPE##_hash_map_t));\ + \ + return map; \ +} \ + \ + \ +static void \ +print_##TYPE##_nodes(TYPE##_hash_map_node_t *node) \ +{ \ + while (node) { \ + fprintf(stderr,"%p ", (void *)node); \ + node = node->next; \ + } \ +} \ + \ +void \ +print_##TYPE##_hash_map(TYPE##_hash_map_t *map) \ +{ \ + int bucket; \ + fprintf(stderr, #TYPE " map %p: base value=%d\n", (void *)map, \ + map->base_value); \ + for (bucket = 0; bucket <= HASH_MAX; bucket++) { \ + fprintf(stderr,"bucket %d: ", bucket); \ + print_##TYPE##_nodes(map->nodes[bucket]); \ + fprintf(stderr,"\n"); \ + } \ + fprintf(stderr,"holes: "); \ + print_##TYPE##_nodes(map->holes); \ + fprintf(stderr,"\n"); \ +} \ + \ +void \ +apply_to_##TYPE##_hash_map(TYPE##_hash_map_t *map, void *param, void (*note)(void *param, TYPE name, int value)) \ +{ \ + int i; \ + for (i = 0; i < HASH_MAX; i++) { \ + TYPE##_hash_map_node_t *node = map->nodes[i]; \ + while (node) { \ + note(param, node->name, node->value); \ + node = node->next; \ + } \ + } \ +} \ + \ + \ +static void \ +free_##TYPE##_hash_map_node_t##_recursive(TYPE##_hash_map_node_t *node) \ +{ \ + if (node) { \ + CLEAR_NODE(node); \ + free_##TYPE##_hash_map_node_t##_recursive(node->next); \ + free(node); \ + } \ +} \ + \ + \ +void \ +free_##TYPE##_hash_map(TYPE##_hash_map_t *map) \ +{ \ + int i; \ + \ + for (i = 0; i <= HASH_MAX; i++) \ + free_##TYPE##_hash_map_node_t##_recursive(map->nodes[i]); \ + \ + free_##TYPE##_hash_map_node_t##_recursive(map->holes); \ + \ + map->base_value = -42000; /* Trigger problems for people who \ + ** forget to loose the reference */ \ + free(map); \ +} \ + \ +int \ +TYPE##_hash_map_check_value(TYPE##_hash_map_t *map, TYPE value, \ + char add, char *was_added) \ +{ \ + TYPE##_hash_map_node_t **node = &(map->nodes[HASH(value)]); \ + \ + while (*node && COMP(value, (*node)->name)) \ + node = &((*node)->next); \ + \ + if (was_added) \ + *was_added = 0; \ + \ + if (*node) { \ + FREE_PARAM(value); \ + return (*node)->value; \ + } \ + /* Not found */ \ + \ + if (!add) \ + return -1; \ + \ + if (was_added) \ + *was_added = 1; \ + \ + if (map->holes) { /* Re-use old node */ \ + (*node) = map->holes; \ + map->holes = (*node)->next; \ + (*node)->next = NULL; \ + (*node)->name = DUP_VALUE(value); \ + } else { \ + *node = (TYPE##_hash_map_node_t*)malloc(sizeof(TYPE##_hash_map_node_t));\ + (*node)->name = DUP_VALUE(value); \ + (*node)->value = map->base_value++; \ + (*node)->next = NULL; \ + } \ + \ + return (*node)->value; \ +} \ + \ + \ +int \ +TYPE##_hash_map_remove_value(TYPE##_hash_map_t *map, TYPE value) \ +{ \ + TYPE##_hash_map_node_t **node = &(map->nodes[HASH(value)]); \ + \ + while (*node && COMP(value, (*node)->name)) \ + node = &((*node)->next); \ + \ + if (*node) { \ + TYPE##_hash_map_node_t *oldnode = *node; \ + *node = (*node)->next; \ + \ + oldnode->next = map->holes; /* Old node is now a 'hole' */ \ + map->holes = oldnode; \ + return oldnode->value; \ + } else return -1; /* Not found */ \ +} \ + diff --git a/engines/sci/scicore/huffmake.pl b/engines/sci/scicore/huffmake.pl new file mode 100644 index 0000000000..8c34537d81 --- /dev/null +++ b/engines/sci/scicore/huffmake.pl @@ -0,0 +1,157 @@ +#! /usr/bin/perl + +# Uncomment the following line to debug +# $DEBUG=1; +@codes; + +$leaf_value = 0; +while (<>) { + chop; + @tokens = split //; + + if ($_ eq "-") { + calc_values(); + print stderr "$leaf_value tokens processed; result is a huffman tree.\n"; + exit(0); + } + + $codes_len[$leaf_value] = scalar @tokens; + + for ($i = 0; $i < scalar @tokens; $i++) { + $codes[$leaf_value][$i] = $tokens[$i]; + } + + $leaf_value++; +} + +$nodes_counter = 0; +@unlinked; + +sub branch_node { + $left = shift; + $right = shift; + print ("\tBRANCH_NODE(", $nodes_counter || "0" , ", $left, $right)\n"); + $nodes_counter++; +} + +sub leaf_node { + $value = shift; + print ("\tLEAF_NODE (", $nodes_counter || "0" ,", ", $value || "0" , ")\n"); + $nodes_counter++; +} + +sub intval { + my $nr = shift; + my $retval = sub_intval(0, $codes_len[$nr], $nr); + return $retval >> 1; +} + +sub sub_intval { + my $lv = shift; + my $maxlv = shift; + my $nr = shift; + + if ($maxlv >= 0) { + my $v = $codes[$nr][$maxlv]; + my $retval = sub_intval($lv + 1, $maxlv-1, $nr) << 1; + + if ($v == "1") { + $retval |= 1; + } + return $retval || 0; + } else { + return 0; + } +} + +sub calc_values() { + + $depth = 1; + my $startdepth = 100000; + + for ($i; $i < scalar @codes; $i++) { + if ($codes_len[$i] > $depth) { + $depth = $codes_len[$i]; + } + + if ($codes_len[$i] < $startdepth) { + $startdepth = $codes_len[$i]; + } + } + + branch_node(1, 2); + + $level = 1; + $unlinked[0] = 1; + $unlinked[1] = 2; + $linkctr = 3; + + for (; $level <= $depth; $level++) { + my $entries = 1 << $level; + + for ($j = 0; $j < ($entries << 1); $j++) { + $new_unlinked[$j] = -1; + } + + for ($i = 0; $i < $entries; $i++) { + if ($unlinked[$i] > -1) { + $match = -1; + + if ($DEBUG) { + print " Finding len=$level val=$i: "; + } + for ($j = 0; ($match == -1) && $j < $leaf_value; $j++) { + if (($codes_len[$j] == $level) + && (intval($j) == $i)) { + $match = $j; + } else { + if ($DEBUG) { + print "($j:$codes_len[$j],",intval($j),") "; + } + } + } + if ($DEBUG) { + print "\n"; + } + + if ($match == -1) { + die "Expected $unlinked[$i], but counted $nodes_counter in $i at level $level" unless ($unlinked[$i] == $nodes_counter); + my $lnr = $linkctr++; + my $rnr = $linkctr++; + $new_unlinked[$i << 1] = $lnr; + $new_unlinked[1+($i << 1)] = $rnr; + branch_node($lnr, $rnr); + } else { + leaf_node($match); + $new_unlinked[$i << 1] = -1; + $new_unlinked[1+($i << 1)] = -1; + } + + } + } + + if ($DEBUG) { + print "level $level: Copying ", ($entries << 1), "\n"; + } + for ($j = 0; $j < ($entries << 1); $j++) { + $unlinked[$j] = $new_unlinked[$j]; + if ($DEBUG) { + print $unlinked[$j], " "; + } + } + if ($DEBUG) { + print "\n"; + } + } + + my $ok = 1; + for ($j = 0; $j < ($entries << 1); $j++) { + if ($unlinked[$j] != -1) { + $ok = 0; + } + } + + print "#warning \"Tree is not a huffman tree!\"\n" unless $ok; +} + + diff --git a/engines/sci/scicore/hufftree.1 b/engines/sci/scicore/hufftree.1 new file mode 100644 index 0000000000..d51462d304 --- /dev/null +++ b/engines/sci/scicore/hufftree.1 @@ -0,0 +1,17 @@ +101 +11 +100 +011 +0101 +0100 +0011 +00101 +00100 +00011 +00010 +000011 +000010 +000001 +0000001 +0000000 +- diff --git a/engines/sci/scicore/hufftree.2 b/engines/sci/scicore/hufftree.2 new file mode 100644 index 0000000000..d13cd2fa66 --- /dev/null +++ b/engines/sci/scicore/hufftree.2 @@ -0,0 +1,69 @@ +11 +1011 +1010 +10011 +10010 +10001 +10000 +011111 +011110 +011101 +011100 +011011 +011010 +011001 +011000 +010111 +010110 +010101 +010100 +010011 +010010 +010001 +0100001 +0100000 +0011111 +0011110 +0011101 +0011100 +0011011 +0011010 +0011001 +0011000 +0010111 +0010110 +0010101 +0010100 +0010011 +0010010 +0010001 +0010000 +0001111 +0001110 +0001101 +0001100 +0001011 +0001010 +0001001 +0001000 +00001111 +00001110 +00001101 +00001100 +00001011 +00001010 +00001001 +00001000 +00000111 +00000110 +00000101 +00000100 +00000011 +00000010 +00000001 +00000000 +- + + + + diff --git a/engines/sci/scicore/hufftree.3 b/engines/sci/scicore/hufftree.3 new file mode 100644 index 0000000000..116b195383 --- /dev/null +++ b/engines/sci/scicore/hufftree.3 @@ -0,0 +1,257 @@ +00001001001 +000001111111 +000001111110 +000001111101 +000001111100 +000001111011 +000001111010 +000001111001 +000001111000 +00011101 +0100011 +000001110111 +000001110110 +0100010 +000001110101 +000001110100 +000001110011 +000001110010 +000001110001 +000001110000 +000001101111 +000001101110 +000001101101 +000001101100 +000001101011 +000001101010 +0000001001001 +000001101001 +000001101000 +000001100111 +000001100110 +000001100101 +1111 +0000101001 +00011100 +000001100100 +0000101000 +000001100011 +0000100111 +00011011 +0100001 +0100000 +00011010 +000011011 +0011111 +100101 +0011110 +00011001 +0011101 +100100 +0011100 +0011011 +0011010 +0011001 +00011000 +0011000 +0010111 +00010111 +00010110 +000001100010 +00001001000 +0010110 +000011010 +00001000111 +000001100001 +100011 +0010101 +100010 +100001 +11101 +0010100 +00010101 +00010100 +100000 +00001000110 +000011001 +011111 +0010011 +011110 +011101 +0010010 +00001000101 +011100 +011011 +011010 +0010001 +000011000 +00010011 +000010111 +000010110 +00001000100 +00010010 +00001000011 +000010101 +000001100000 +00010001 +000001011111 +11100 +011001 +011000 +010111 +11011 +010110 +010101 +010100 +11010 +00001000010 +0010000 +11001 +010011 +11000 +10111 +010010 +0000100110 +10110 +10101 +10100 +10011 +00010000 +0001111 +00001111 +00001110 +0000100101 +00001000001 +00001000000 +000001011110 +000001011101 +000001011100 +0000001001000 +0000001000111 +0000001000110 +0000001000101 +0000001000100 +0000001000011 +0000001000010 +0000001000001 +0000001000000 +0000000111111 +0000000111110 +0000000111101 +0000000111100 +0000000111011 +0000000111010 +0000000111001 +0000000111000 +0000000110111 +0000000110110 +0000000110101 +0000000110100 +0000000110011 +0000000110010 +0000000110001 +0000000110000 +0000000101111 +0000000101110 +0000000101101 +0000000101100 +0000000101011 +0000000101010 +0000000101001 +0000000101000 +0000000100111 +0000000100110 +0000000100101 +0000000100100 +0000000100011 +0000000100010 +0000000100001 +0000000100000 +0000000011111 +0000000011110 +0000000011101 +0000000011100 +0000000011011 +0000000011010 +0000000011001 +000001011011 +000001011010 +000001011001 +000001011000 +000001010111 +000001010110 +000001010101 +000001010100 +000001010011 +000001010010 +000001010001 +000001010000 +000001001111 +000001001110 +000001001101 +000001001100 +000001001011 +000001001010 +000001001001 +000001001000 +000001000111 +000001000110 +000001000101 +000001000100 +000001000011 +000001000010 +000001000001 +000001000000 +000000111111 +000000111110 +000000111101 +000000111100 +000000111011 +000000111010 +000000111001 +000000111000 +000000110111 +000000110110 +000000110101 +000000110100 +000000110011 +000000110010 +000000110001 +000000110000 +000000101111 +000000101110 +000000101101 +000000101100 +0000000011000 +000000101011 +0000000010111 +0000000010110 +0000000010101 +000000101010 +0000000010100 +0000000010011 +0000000010010 +000000101001 +0000000010001 +0000000010000 +0000000001111 +0000000001110 +000000101000 +0000000001101 +0000000001100 +0000000001011 +000000100111 +000000100110 +000000100101 +0000000001010 +0000000001001 +0000000001000 +0000000000111 +0000000000110 +0000000000101 +0000000000100 +0000000000011 +0000000000010 +0000000000001 +0000000000000 +- diff --git a/engines/sci/scicore/int_hashmap.c b/engines/sci/scicore/int_hashmap.c new file mode 100644 index 0000000000..0b70838e17 --- /dev/null +++ b/engines/sci/scicore/int_hashmap.c @@ -0,0 +1,33 @@ +/*************************************************************************** + int_hashmap. Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#define BUILD_MAP_FUNCTIONS +#include "int_hashmap.h" + +#include "hashmap.c" + +DEFINE_FUNCTIONS(int) diff --git a/engines/sci/scicore/makefile.dos b/engines/sci/scicore/makefile.dos new file mode 100644 index 0000000000..d456fb16de --- /dev/null +++ b/engines/sci/scicore/makefile.dos @@ -0,0 +1,19 @@ +# +# FreeSCI/DOS Makefile +# +# 19991220 rink created this file +# +# +TARGET : scicore.a + +FILES = console.o tools.o resource.o decompress0.o decompress1.o \ + script.o vocab.o vocab_debug.o old_objects.o sci_dos.o + +CC = gcc +CFLAGS = -g -c -I../include -I../.. -D_DOS -DHAVE_LIBPNG -DHAVE_UNISTD_H + +clean: + del *.o *.a + +scicore.a: ${FILES} + ar r scicore.a ${FILES} diff --git a/engines/sci/scicore/modules.c b/engines/sci/scicore/modules.c new file mode 100644 index 0000000000..f63d4e4aa0 --- /dev/null +++ b/engines/sci/scicore/modules.c @@ -0,0 +1,153 @@ +/*************************************************************************** + modules.c Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#if 0 +#ifndef __BEOS__ + +#include +#include +#include + + +static sci_module_t * +_sci_try_open_module(char *filename, char *path, char *struct_name, void **handle) +{ + char *fullname = sci_malloc(strlen(path) + strlen(DIR_SEPARATOR_STR) + + strlen(filename)); + sci_module_t *module; +fprintf(stderr,"Trying module %s at %s\n", filename, path); + strcpy(fullname, path); + strcat(fullname, DIR_SEPARATOR_STR); + strcat(fullname, filename); + +fprintf(stderr,"Total name is %s\n", fullname); + *handle = dlopen(fullname, RTLD_NOW); +fprintf(stderr,"Could not open because: %s\n", dlerror()); + free(fullname); + + if (!*handle) + return NULL; + + module = (sci_module_t *) dlsym(*handle, struct_name); + if (!module) + fprintf(stderr,"%s: Failed to find symbol '%s'.\n", + fullname, struct_name); + + return module; +} + +void * +sci_find_module(char *path, char *name, char *type, char *struct_prefix, + char *file_suffix, int magic, int version, void **handle) +{ + char *module_name = sci_malloc(strlen(type) + strlen(DIR_SEPARATOR_STR) + + strlen(name) + strlen(file_suffix) + + strlen(MODULE_NAME_SUFFIX) + 1); + char *struct_name = sci_malloc(strlen(struct_prefix) + strlen(name) + 1); + char *dir_end; + char *path_pos = path; + char path_separator = PATH_SEPARATOR_STR[0]; + sci_module_t *module = NULL; + + strcpy(module_name, type); + strcat(module_name, DIR_SEPARATOR_STR); + strcat(module_name, name); + strcat(module_name, file_suffix); + strcat(module_name, MODULE_NAME_SUFFIX); + + strcpy(struct_name, struct_prefix); + strcat(struct_name, name); + + do { + dir_end = strchr(path_pos, path_separator); + if (dir_end) + *dir_end = 0; + + module = _sci_try_open_module(module_name, path_pos, + struct_name, handle); + + if (module) { + if (module->class_magic != magic) { + fprintf(stderr, "%s at %s is not a %s module, skipping...\n", + module_name, path_pos, type); + dlclose(*handle); + module = NULL; + } else if (module->class_version != version) { + fprintf(stderr, "%s at %s has %s module version %d," + " expected %d- skipping...\n", + module_name, path_pos, type, module->class_version, + version); + dlclose(*handle); + module = NULL; + } + } + + if (dir_end) { + *dir_end = path_separator; + path_pos = dir_end + 1; + } + + } while (!module && dir_end); + + if (!module) { + *handle = NULL; + fprintf(stderr,"%s module '%s' not found in path '%s'.\n", + type, name, path); + } else { + if (dir_end) + *dir_end = 0; + + printf("Using %s driver '%s', version %s, from '%s'.\n", + type, module->module_name, module->module_version, + path_pos); + + if (dir_end) + *dir_end = path_separator; + } + + free(module_name); + free(struct_name); + + return (void *) module; +} + + +void +sci_close_module(void *module, char *type, char *name) +{ + if (!module) + return; + + if (dlclose(module)) { + fprintf(stderr,"Error while closing %s module '%s': %s\n", + type, name, dlerror()); + } +} + +#endif /* !__BEOS__ */ + +#endif /* 0 */ diff --git a/engines/sci/scicore/old_objects.c b/engines/sci/scicore/old_objects.c new file mode 100644 index 0000000000..8408373537 --- /dev/null +++ b/engines/sci/scicore/old_objects.c @@ -0,0 +1,716 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(imethods[meth]; + int i, j; + + for(j=0; jnumber]); + + for(i=0; iused; i++) + { + script_opcode op=m->data[i]; + + for(j=0; jknames_count) printf(" ", op.arg1); + else printf("%s ", knames[op.arg1]); + printf("%02X", op.arg2); + } break; + case 0x28: /*class*/ + { + if(op.arg1>max_object) printf("", op.arg1); + else + { + /* [DJ] op.arg1+1 adjusts for the object */ + if(fobjects.data[op.arg1+1]==0) printf(""); + else printf("%s", fobjects.data[op.arg1+1]->name); + } + } break; + case 0x44: + { + if(op.arg1>0x20) printf(" ", 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(" "); + } 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(" ", formats[op.opcode][j]); + } + } + } + } break; + } + printf("\n"); + } +} + +static void printObject_r(object* obj, int flags, int level) +{ + int i; + for(i=0; iname); + if(flags&SCRIPT_PRINT_METHODS) + { + for(i=0; imethod_count; i++) + { + printMethod(obj, i, level+1); + } + } + if(flags&SCRIPT_PRINT_CHILDREN) + { + for(i=0; ichildren.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; iused-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(possize-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(selsize-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 && \ + !((0data)) && (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=""; + else + { + if (get_selector_value(raw, 3)) + obj->name = (char *) r->data + get_selector_value(raw, 3); + else obj->name=""; + } + + /*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; imethod_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(""); + + /*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(""); + 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=""; + else + { + if (get_selector_value(raw, 3)) + obj->name = (char *) r->data + get_selector_value(raw, 3); + else obj->name=""; + } + + /*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(""); + } + 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(""); + } + if(add_child(obj->parent, obj)){ + free(obj); + return fake_object(""); + } + if(add_object(obj)) + { + free(obj); + return fake_object(""); + } + + /*FIXME: decode selectors and methods here*/ + + return obj; +} + +void freeObject(object* obj) +{ + int i; + for(i=0; ichildren.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=""; + 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 + +***************************************************************************/ + +#define BUILD_MAP_FUNCTIONS +#include "reg_t_hashmap.h" + +#include "hashmap.c" + +static inline int +compare_reg_t (reg_t lhs, reg_t rhs) +{ + if (lhs.segment == rhs.segment) + return lhs.offset - rhs.offset; + else + return lhs.segment - rhs.segment; +} + +DEFINE_FUNCTIONS(reg_t) diff --git a/engines/sci/scicore/resource.c b/engines/sci/scicore/resource.c new file mode 100644 index 0000000000..b137790f89 --- /dev/null +++ b/engines/sci/scicore/resource.c @@ -0,0 +1,951 @@ +/*************************************************************************** + resource.c Copyright (C) 1999 Christoph Reichenbach, TU Darmstadt + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + + History: + + 990327 - created (CJR) + +***************************************************************************/ +/* Resource library */ + +#include +#include +#include /* For SCI version auto-detection */ + +#include + +#ifdef _WIN32 +#include +#endif + +#undef SCI_REQUIRE_RESOURCE_FILES +/* #define SCI_VERBOSE_RESMGR 1 */ + +const char* sci_version_types[] = { + "SCI version undetermined (Autodetect failed / not run)", + "SCI version 0.xxx", + "SCI version 0.xxx w/ 1.000 compression", + "SCI version 1.000 w/ 0.xxx resource.map", + "SCI version 1.000 w/ special resource.map", + "SCI version 1.000 (early)", + "SCI version 1.000 (late)", + "SCI version 1.001", + "SCI WIN/32" +}; + +const int sci_max_resource_nr[] = {65536, 1000, 2048, 2048, 2048, 8192, 8192, 65536}; + +const char* sci_error_types[] = { + "No error", + "I/O error", + "Resource is empty (size 0)", + "resource.map entry is invalid", + "resource.map file not found", + "No resource files found", + "Unknown compression method", + "Decompression failed: Decompression buffer overflow", + "Decompression failed: Sanity check failed", + "Decompression failed: Resource too big", + "SCI version is unsupported"}; + +const char* sci_resource_types[] = {"view","pic","script","text","sound", + "memory","vocab","font","cursor", + "patch","bitmap","palette","cdaudio", + "audio","sync","message","map","heap"}; +/* These are the 18 resource types supported by SCI1 */ + +const char *sci_resource_type_suffixes[] = {"v56","p56","scr","tex","snd", + " ","voc","fon","cur","pat", + "bit","pal","cda","aud","syn", + "msg","map","hep"}; + + +int resourcecmp(const void *first, const void *second); + + +typedef int decomp_funct(resource_t *result, int resh, int sci_version); +typedef void patch_sprintf_funct(char *string, resource_t *res); + +static decomp_funct *decompressors[] = { + NULL, + &decompress0, + &decompress01, + &decompress01, + &decompress01, + &decompress1, + &decompress1, + &decompress11, + NULL +}; + +static patch_sprintf_funct *patch_sprintfers[] = { + NULL, + &sci0_sprintf_patch_file_name, + &sci0_sprintf_patch_file_name, + &sci1_sprintf_patch_file_name, + &sci1_sprintf_patch_file_name, + &sci1_sprintf_patch_file_name, + &sci1_sprintf_patch_file_name, + &sci1_sprintf_patch_file_name, + &sci1_sprintf_patch_file_name +}; + + +int resourcecmp (const void *first, const void *second) +{ + if (((resource_t *)first)->type == + ((resource_t *)second)->type) + return (((resource_t *)first)->number < + ((resource_t *)second)->number)? -1 : + !(((resource_t *)first)->number == + ((resource_t *)second)->number); + else + return (((resource_t *)first)->type < + ((resource_t *)second)->type)? -1 : 1; +} + + + + + +/*-----------------------------*/ +/*-- Resmgr helper functions --*/ +/*-----------------------------*/ + +void +_scir_add_altsource(resource_t *res, resource_source_t *source, unsigned int file_offset) +{ + resource_altsource_t *rsrc = (resource_altsource_t*)sci_malloc(sizeof(resource_altsource_t)); + + rsrc->next = res->alt_sources; + rsrc->source = source; + rsrc->file_offset = file_offset; + res->alt_sources = rsrc; +} + +resource_t * +_scir_find_resource_unsorted(resource_t *res, int res_nr, int type, int number) +{ + int i; + for (i = 0; i < res_nr; i++) + if (res[i].number == number && res[i].type == type) + return res + i; + return NULL; +} + +/*-----------------------------------*/ +/** Resource source list management **/ +/*-----------------------------------*/ + +resource_source_t * +scir_add_external_map(resource_mgr_t *mgr, char *file_name) +{ + resource_source_t *newsrc = (resource_source_t *) + malloc(sizeof(resource_source_t)); + + /* Add the new source to the SLL of sources */ + newsrc->next = mgr->sources; + mgr->sources = newsrc; + + newsrc->source_type = RESSOURCE_TYPE_EXTERNAL_MAP; + newsrc->location.file.name = strdup(file_name); + newsrc->scanned = 0; + newsrc->associated_map = NULL; + + return newsrc; +} + +resource_source_t * +scir_add_volume(resource_mgr_t *mgr, resource_source_t *map, char *filename, + int number, int extended_addressing) +{ + resource_source_t *newsrc = (resource_source_t *) + malloc(sizeof(resource_source_t)); + + /* Add the new source to the SLL of sources */ + newsrc->next = mgr->sources; + mgr->sources = newsrc; + + newsrc->source_type = RESSOURCE_TYPE_VOLUME; + newsrc->scanned = 0; + newsrc->location.file.name = strdup(filename); + newsrc->location.file.volume_number = number; + newsrc->associated_map = map; +} + +resource_source_t * +scir_add_patch_dir(resource_mgr_t *mgr, int type, char *dirname) +{ + resource_source_t *newsrc = (resource_source_t *) + malloc(sizeof(resource_source_t)); + + /* Add the new source to the SLL of sources */ + newsrc->next = mgr->sources; + mgr->sources = newsrc; + + newsrc->source_type = RESSOURCE_TYPE_DIRECTORY; + newsrc->scanned = 0; + newsrc->location.dir.name = strdup(dirname); +} + +resource_source_t * +scir_get_volume(resource_mgr_t *mgr, resource_source_t *map, int volume_nr) +{ + resource_source_t *seeker = mgr->sources; + + while (seeker) + { + if (seeker->source_type == RESSOURCE_TYPE_VOLUME && + seeker->associated_map == map && + seeker->location.file.volume_number == volume_nr) + return seeker; + seeker = seeker->next; + } + + return NULL; +} + +/*------------------------------------------------*/ +/** Resource manager constructors and operations **/ +/*------------------------------------------------*/ + +static void +_scir_init_trivial(resource_mgr_t *mgr) +{ + mgr->resources_nr = 0; + mgr->resources = (resource_t*)sci_malloc(1); +} + + +static void +_scir_load_from_patch_file(int fh, resource_t *res, char *filename) +{ + int really_read; + + res->data = (unsigned char*)sci_malloc(res->size); + really_read = read(fh, res->data, res->size); + + if (really_read < res->size) { + sciprintf("Error: Read %d bytes from %s but expected %d!\n", + really_read, filename, res->size); + exit(1); + } + + res->status = SCI_STATUS_ALLOCATED; +} + +static void +_scir_load_resource(resource_mgr_t *mgr, resource_t *res, int protect) +{ + char filename[PATH_MAX]; + int fh; + resource_t backup; + char *save_cwd = sci_getcwd(); + + memcpy(&backup, res, sizeof(resource_t)); + + /* First try lower-case name */ + if (res->source->source_type == RESSOURCE_TYPE_DIRECTORY) { + + if (!patch_sprintfers[mgr->sci_version]) { + sciprintf("Resource manager's SCI version (%d) has no patch file name printers -> internal error!\n", + mgr->sci_version); + exit(1); + } + + /* Get patch file name */ + patch_sprintfers[mgr->sci_version](filename, res); + chdir(res->source->location.dir.name); + } else + strcpy(filename, res->source->location.file.name); + + fh = open(filename, O_RDONLY | O_BINARY); + + + if (!IS_VALID_FD(fh)) { + char *raiser = filename; + while (*raiser) { + *raiser = toupper(*raiser); /* Uppercasify */ + ++raiser; + } + fh = sci_open(filename, O_RDONLY|O_BINARY); + } /* Try case-insensitively name */ + + if (!IS_VALID_FD(fh)) { + sciprintf("Failed to open %s!\n", filename); + res->data = NULL; + res->status = SCI_STATUS_NOMALLOC; + res->size = 0; + chdir(save_cwd); + free(save_cwd); + return; + } + + + lseek(fh, res->file_offset, SEEK_SET); + + if (res->source->source_type == RESSOURCE_TYPE_DIRECTORY || + res->source->source_type == RESSOURCE_TYPE_AUDIO_DIRECTORY) + _scir_load_from_patch_file(fh, res, filename); + else if (!decompressors[mgr->sci_version]) { + /* Check whether we support this at all */ + sciprintf("Resource manager's SCI version (%d) is invalid!\n", + mgr->sci_version); + exit(1); + } else { + int error = /* Decompress from regular resource file */ + decompressors[mgr->sci_version](res, fh, mgr->sci_version); + + if (error) { + sciprintf("Error %d occured while reading %s.%03d" + " from resource file: %s\n", + error, sci_resource_types[res->type], res->number, + sci_error_types[error]); + + if (protect) + memcpy(res, &backup, sizeof(resource_t)); + + res->data = NULL; + res->status = SCI_STATUS_NOMALLOC; + res->size = 0; + chdir(save_cwd); + free(save_cwd); + return; + } + } + + close(fh); +} + +resource_t * +scir_test_resource(resource_mgr_t *mgr, int type, int number) +{ + resource_t binseeker; + binseeker.type = type; + binseeker.number = number; + return (resource_t *) + bsearch(&binseeker, mgr->resources, mgr->resources_nr, + sizeof(resource_t), resourcecmp); +} + +int sci0_get_compression_method(int resh); + +int +sci_test_view_type(resource_mgr_t *mgr) +{ + int fh; + char filename[PATH_MAX]; + int compression; + resource_t *res; + int i; + + mgr->sci_version = SCI_VERSION_AUTODETECT; + + for (i=0;i<1000;i++) + { + res = scir_test_resource(mgr, sci_view, i); + + if (!res) continue; + + if (res->source->source_type == RESSOURCE_TYPE_DIRECTORY || + res->source->source_type == RESSOURCE_TYPE_AUDIO_DIRECTORY) + continue; + + strcpy(filename, res->source->location.file.name); + fh = open(filename, O_RDONLY | O_BINARY); + + if (!IS_VALID_FD(fh)) { + char *raiser = filename; + while (*raiser) { + *raiser = toupper(*raiser); /* Uppercasify */ + ++raiser; + } + fh = sci_open(filename, O_RDONLY|O_BINARY); + } /* Try case-insensitively name */ + + if (!IS_VALID_FD(fh)) continue; + lseek(fh, res->file_offset, SEEK_SET); + + compression = sci0_get_compression_method(fh); + close(fh); + + if (compression == 3) + return (mgr->sci_version = SCI_VERSION_01_VGA); + } + + /* Try the same thing with pics */ + for (i=0;i<1000;i++) + { + res = scir_test_resource(mgr, sci_pic, i); + + if (!res) continue; + + if (res->source->source_type == RESSOURCE_TYPE_DIRECTORY || + res->source->source_type == RESSOURCE_TYPE_AUDIO_DIRECTORY) + continue; + + strcpy(filename, res->source->location.file.name); + fh = open(filename, O_RDONLY | O_BINARY); + + + if (!IS_VALID_FD(fh)) { + char *raiser = filename; + while (*raiser) { + *raiser = toupper(*raiser); /* Uppercasify */ + ++raiser; + } + fh = sci_open(filename, O_RDONLY|O_BINARY); + } /* Try case-insensitively name */ + + if (!IS_VALID_FD(fh)) continue; + lseek(fh, res->file_offset, SEEK_SET); + + compression = sci0_get_compression_method(fh); + close(fh); + + if (compression == 3) + return (mgr->sci_version = SCI_VERSION_01_VGA); + } + + return mgr->sci_version; +} + + + +int +scir_add_appropriate_sources(resource_mgr_t *mgr, + int allow_patches, + char *dir) +{ + char *trailing_slash = ""; + char path_separator; + sci_dir_t dirent; + char *name; + resource_source_t *map; + int fd; + char fullname[PATH_MAX]; + + if (dir[strlen(dir)-1] != G_DIR_SEPARATOR) + { + trailing_slash = G_DIR_SEPARATOR_S; + } + + name = (char *)malloc(strlen(dir) + 1 + + strlen("RESOURCE.MAP") + 1); + + sprintf(fullname, "%s%s%s", dir, trailing_slash, "RESOURCE.MAP"); + fd = sci_open("RESOURCE.MAP", O_RDONLY | O_BINARY); + if (!IS_VALID_FD(fd)) return 0; + close(fd); + map = scir_add_external_map(mgr, fullname); + free(name); + sci_init_dir(&dirent); + name = sci_find_first(&dirent, "RESOURCE.0??"); + while (name != NULL) + { + char *dot = strrchr(name, '.'); + int number = atoi(dot + 1); + + sprintf(fullname, "%s%s%s", dir, G_DIR_SEPARATOR_S, name); + scir_add_volume(mgr, map, fullname, number, 0); + name = sci_find_next(&dirent); + } + sci_finish_find(&dirent); + + sci_finish_find(&dirent); + sprintf(fullname, "%s%s", dir, G_DIR_SEPARATOR_S); + scir_add_patch_dir(mgr, RESSOURCE_TYPE_DIRECTORY, fullname); + + return 1; +} + +static int +_scir_scan_new_sources(resource_mgr_t *mgr, int *detected_version, resource_source_t *source) +{ + int preset_version = mgr->sci_version; + int resource_error = 0; + int dummy = mgr->sci_version; + resource_t **concat_ptr = &(mgr->resources[mgr->resources_nr-1].next); + + if (detected_version == NULL) + detected_version = &dummy; + + *detected_version = mgr->sci_version; + if (source->next) + _scir_scan_new_sources(mgr, detected_version, source->next); + + if (!source->scanned) + { + source->scanned = 1; + switch (source->source_type) + { + case RESSOURCE_TYPE_DIRECTORY: + if (mgr->sci_version <= SCI_VERSION_01) + sci0_read_resource_patches(source, + &mgr->resources, + &mgr->resources_nr); + else + sci1_read_resource_patches(source, + &mgr->resources, + &mgr->resources_nr); + break; + case RESSOURCE_TYPE_EXTERNAL_MAP: + if (preset_version <= SCI_VERSION_01_VGA_ODD + /* || preset_version == SCI_VERSION_AUTODETECT -- subsumed by the above line */) { + resource_error = + sci0_read_resource_map(mgr, + source, + &mgr->resources, + &mgr->resources_nr, + detected_version); + +#if 0 + if (resource_error >= SCI_ERROR_CRITICAL) { + sciprintf("Resmgr: Error while loading resource map: %s\n", + sci_error_types[resource_error]); + if (resource_error == SCI_ERROR_RESMAP_NOT_FOUND) + sciprintf("Running SCI games without a resource map is not supported ATM\n"); + sci_free(mgr); + chdir(caller_cwd); + free(caller_cwd); + return NULL; + } + if (resource_error == SCI_ERROR_RESMAP_NOT_FOUND) { + /* fixme: Try reading w/o resource.map */ + resource_error = SCI_ERROR_NO_RESOURCE_FILES_FOUND; + } + + if (resource_error == SCI_ERROR_NO_RESOURCE_FILES_FOUND) { + /* Initialize empty resource manager */ + _scir_init_trivial(mgr); + resource_error = 0; + } +#endif + } + + if ((preset_version == SCI_VERSION_1_EARLY)|| + (preset_version == SCI_VERSION_1_LATE)|| + (preset_version == SCI_VERSION_1_1)|| + ((*detected_version == SCI_VERSION_AUTODETECT)&&(preset_version == SCI_VERSION_AUTODETECT))) + { + resource_error = + sci1_read_resource_map(mgr, + source, + scir_get_volume(mgr, source, 0), + &mgr->resources, + &mgr->resources_nr, + detected_version); + + if (resource_error == SCI_ERROR_RESMAP_NOT_FOUND) { + /* fixme: Try reading w/o resource.map */ + resource_error = SCI_ERROR_NO_RESOURCE_FILES_FOUND; + } + + if (resource_error == SCI_ERROR_NO_RESOURCE_FILES_FOUND) { + /* Initialize empty resource manager */ + _scir_init_trivial(mgr); + resource_error = 0; + } + + *detected_version = SCI_VERSION_1; + } + + mgr->sci_version = *detected_version; + break; + } + qsort(mgr->resources, mgr->resources_nr, sizeof(resource_t), + resourcecmp); /* Sort resources */ + } + return resource_error; +} + +int +scir_scan_new_sources(resource_mgr_t *mgr, int *detected_version) +{ + _scir_scan_new_sources(mgr, detected_version, mgr->sources); +} + +static void +_scir_free_resource_sources(resource_source_t *rss) +{ + if (rss) { + _scir_free_resource_sources(rss->next); + free(rss); + } +} + +resource_mgr_t * +scir_new_resource_manager(char *dir, int version, + char allow_patches, int max_memory) +{ + int resource_error = 0; + resource_mgr_t *mgr = (resource_mgr_t*)sci_malloc(sizeof(resource_mgr_t)); + char *caller_cwd = sci_getcwd(); + int resmap_version = version; + + if (chdir(dir)) { + sciprintf("Resmgr: Directory '%s' is invalid!\n", dir); + free(caller_cwd); + return NULL; + } + + mgr->max_memory = max_memory; + + mgr->memory_locked = 0; + mgr->memory_lru = 0; + + mgr->resource_path = dir; + + mgr->resources = NULL; + mgr->resources_nr = 0; + mgr->sources = NULL; + mgr->sci_version = version; + + scir_add_appropriate_sources(mgr, allow_patches, dir); + scir_scan_new_sources(mgr, &resmap_version); + + if (!mgr->resources || !mgr->resources_nr) { + if (mgr->resources) { + free(mgr->resources); + mgr->resources = NULL; + } + sciprintf("Resmgr: Could not retreive a resource list!\n"); + _scir_free_resource_sources(mgr->sources); + sci_free(mgr); + chdir(caller_cwd); + free(caller_cwd); + return NULL; + } + + mgr->lru_first = NULL; + mgr->lru_last = NULL; + + mgr->allow_patches = allow_patches; + + qsort(mgr->resources, mgr->resources_nr, sizeof(resource_t), + resourcecmp); /* Sort resources */ + + if (version == SCI_VERSION_AUTODETECT) + switch (resmap_version) { + case SCI_VERSION_0: + if (scir_test_resource(mgr, sci_vocab, + VOCAB_RESOURCE_SCI0_MAIN_VOCAB)) { + version = sci_test_view_type(mgr); + if (version == SCI_VERSION_01_VGA) + { + sciprintf("Resmgr: Detected KQ5 or similar\n"); + } else { + sciprintf("Resmgr: Detected SCI0\n"); + version = SCI_VERSION_0; + } + } else if (scir_test_resource(mgr, sci_vocab, + VOCAB_RESOURCE_SCI1_MAIN_VOCAB)) { + version = sci_test_view_type(mgr); + if (version == SCI_VERSION_01_VGA) + { + sciprintf("Resmgr: Detected KQ5 or similar\n"); + } else { + if (scir_test_resource(mgr, sci_vocab, 912)) { + sciprintf("Resmgr: Running KQ1 or similar, using SCI0 resource encoding\n"); + version = SCI_VERSION_0; + } else { + version = SCI_VERSION_01; + sciprintf("Resmgr: Detected SCI01\n"); + } + } + } else { + version = sci_test_view_type(mgr); + if (version == SCI_VERSION_01_VGA) + { + sciprintf("Resmgr: Detected KQ5 or similar\n"); + } else { + sciprintf("Resmgr: Warning: Could not find vocabulary; assuming SCI0 w/o parser\n"); + version = SCI_VERSION_0; + } + } break; + case SCI_VERSION_01_VGA_ODD: + version = resmap_version; + sciprintf("Resmgr: Detected Jones/CD or similar\n"); + break; + case SCI_VERSION_1: + { + resource_t *res = scir_test_resource(mgr, sci_script, 0); + + mgr->sci_version = version = SCI_VERSION_1_EARLY; + _scir_load_resource(mgr, res, 1); + + if (res->status == SCI_STATUS_NOMALLOC) + mgr->sci_version = version = SCI_VERSION_1_LATE; + + /* No need to handle SCI 1.1 here - it was done in resource_map.c */ + break; + } + default: + sciprintf("Resmgr: Warning: While autodetecting: Couldn't" + " determine SCI version!\n"); + } + + if (!resource_error) + { +#if 0 + if (version <= SCI_VERSION_01) + sci0_read_resource_patches(dir, + &mgr->resources, + &mgr->resources_nr); + else + sci1_read_resource_patches(dir, + &mgr->resources, + &mgr->resources_nr); +#endif + + qsort(mgr->resources, mgr->resources_nr, sizeof(resource_t), + resourcecmp); /* Sort resources */ + } + + mgr->sci_version = version; + + chdir(caller_cwd); + free(caller_cwd); + + return mgr; +} + +static void +_scir_free_altsources(resource_altsource_t *dynressrc) +{ + if (dynressrc) { + _scir_free_altsources(dynressrc->next); + free(dynressrc); + } +} + +void +_scir_free_resources(resource_t *resources, int resources_nr) +{ + int i; + + for (i = 0; i < resources_nr; i++) { + resource_t *res = resources + i; + + _scir_free_altsources(res->alt_sources); + + if (res->status != SCI_STATUS_NOMALLOC) + sci_free(res->data); + } + + sci_free(resources); +} + +void +scir_free_resource_manager(resource_mgr_t *mgr) +{ + _scir_free_resources(mgr->resources, mgr->resources_nr); + _scir_free_resource_sources(mgr->sources); + mgr->resources = NULL; + + sci_free(mgr); +} + + +static void +_scir_unalloc(resource_t *res) +{ + sci_free(res->data); + res->data = NULL; + res->status = SCI_STATUS_NOMALLOC; +} + + +static void +_scir_remove_from_lru(resource_mgr_t *mgr, resource_t *res) +{ + if (res->status != SCI_STATUS_ENQUEUED) { + sciprintf("Resmgr: Oops: trying to remove resource that isn't" + " enqueued\n"); + return; + } + + if (res->next) + res->next->prev = res->prev; + if (res->prev) + res->prev->next = res->next; + if (mgr->lru_first == res) + mgr->lru_first = res->next; + if (mgr->lru_last == res) + mgr->lru_last = res->prev; + + mgr->memory_lru -= res->size; + + res->status = SCI_STATUS_ALLOCATED; +} + +static void +_scir_add_to_lru(resource_mgr_t *mgr, resource_t *res) +{ + if (res->status != SCI_STATUS_ALLOCATED) { + sciprintf("Resmgr: Oops: trying to enqueue resource with state" + " %d\n", res->status); + return; + } + + res->prev = NULL; + res->next = mgr->lru_first; + mgr->lru_first = res; + if (!mgr->lru_last) + mgr->lru_last = res; + if (res->next) + res->next->prev = res; + + mgr->memory_lru += res->size; +#if (SCI_VERBOSE_RESMGR > 1) + fprintf(stderr, "Adding %s.%03d (%d bytes) to lru control: %d bytes total\n", + sci_resource_types[res->type], res->number, res->size, + mgr->memory_lru); + +#endif + + res->status = SCI_STATUS_ENQUEUED; +} + +static void +_scir_print_lru_list(resource_mgr_t *mgr) +{ + int mem = 0; + int entries = 0; + resource_t *res = mgr->lru_first; + + while (res) { + fprintf(stderr,"\t%s.%03d: %d bytes\n", + sci_resource_types[res->type], res->number, + res->size); + mem += res->size; + ++entries; + res = res->next; + } + + fprintf(stderr,"Total: %d entries, %d bytes (mgr says %d)\n", + entries, mem, mgr->memory_lru); +} + +static void +_scir_free_old_resources(resource_mgr_t *mgr, int last_invulnerable) +{ + while (mgr->max_memory < mgr->memory_lru + && (!last_invulnerable || mgr->lru_first != mgr->lru_last)) { + resource_t *goner = mgr->lru_last; + if (!goner) { + fprintf(stderr,"Internal error: mgr->lru_last is NULL!\n"); + fprintf(stderr,"LRU-mem= %d\n", mgr->memory_lru); + fprintf(stderr,"lru_first = %p\n", (void *)mgr->lru_first); + _scir_print_lru_list(mgr); + } + + _scir_remove_from_lru(mgr, goner); + _scir_unalloc(goner); +#ifdef SCI_VERBOSE_RESMGR + sciprintf("Resmgr-debug: LRU: Freeing %s.%03d (%d bytes)\n", + sci_resource_types[goner->type], goner->number, + goner->size); +#endif + } +} + +resource_t * +scir_find_resource(resource_mgr_t *mgr, int type, int number, int lock) +{ + resource_t *retval; + + if (number >= sci_max_resource_nr[mgr->sci_version]) { + int modded_number = number % sci_max_resource_nr[mgr->sci_version]; + sciprintf("[resmgr] Requested invalid resource %s.%d, mapped to %s.%d\n", + sci_resource_types[type], number, + sci_resource_types[type], modded_number); + number = modded_number; + } + + retval = scir_test_resource(mgr, type, number); + + if (!retval) + return NULL; + + if (!retval->status) + _scir_load_resource(mgr, retval, 0); + + else if (retval->status == SCI_STATUS_ENQUEUED) + _scir_remove_from_lru(mgr, retval); + /* Unless an error occured, the resource is now either + ** locked or allocated, but never queued or freed. */ + + if (lock) { + if (retval->status == SCI_STATUS_ALLOCATED) { + retval->status = SCI_STATUS_LOCKED; + retval->lockers = 0; + mgr->memory_locked += retval->size; + } + + ++retval->lockers; + + } else if (retval->status != SCI_STATUS_LOCKED) { /* Don't lock it */ + if (retval->status == SCI_STATUS_ALLOCATED) + _scir_add_to_lru(mgr, retval); + } + + _scir_free_old_resources(mgr, retval->status == SCI_STATUS_ALLOCATED); + + if (retval->data) + return retval; + else { + sciprintf("Resmgr: Failed to read %s.%03d\n", + sci_resource_types[retval->type], retval->number); + return NULL; + } +} + +void +scir_unlock_resource(resource_mgr_t *mgr, resource_t *res, int resnum, int restype) +{ + if (!res) { + sciprintf("Resmgr: Warning: Attempt to unlock non-existant" + " resource %s.%03d!\n", + sci_resource_types[restype], resnum); + return; + } + + if (res->status != SCI_STATUS_LOCKED) { + sciprintf("Resmgr: Warning: Attempt to unlock unlocked" + " resource %s.%03d\n", + sci_resource_types[res->type], res->number); + return; + } + + if (!--res->lockers) { /* No more lockers? */ + res->status = SCI_STATUS_ALLOCATED; + mgr->memory_locked -= res->size; + _scir_add_to_lru(mgr, res); + } + + _scir_free_old_resources(mgr, 0); +} + diff --git a/engines/sci/scicore/resource_map.c b/engines/sci/scicore/resource_map.c new file mode 100644 index 0000000000..5a85383911 --- /dev/null +++ b/engines/sci/scicore/resource_map.c @@ -0,0 +1,515 @@ +/*************************************************************************** + resource_map.c Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include +#include +#ifdef HAVE_UNISTD_H +# include +#endif + +#define RESOURCE_MAP_FILENAME "resource.map" + +#define SCI0_RESMAP_ENTRIES_SIZE 6 +#define SCI1_RESMAP_ENTRIES_SIZE 6 +#define SCI11_RESMAP_ENTRIES_SIZE 5 + +static int +detect_odd_sci01(int fh) +{ + byte buf[6]; + int files_ok = 1; + int fsize, resources_nr, tempfh, read_ok; + char filename[14]; + + fsize = sci_fd_size(fh); + if (fsize < 0) { + perror("Error occured while trying to get filesize of resource.map"); + return SCI_ERROR_RESMAP_NOT_FOUND; + } + + resources_nr = fsize / SCI0_RESMAP_ENTRIES_SIZE; + + while (resources_nr-->1) + { + read_ok = read(fh, &buf, SCI0_RESMAP_ENTRIES_SIZE); + + if (read_ok) + { + sprintf(filename, "resource.%03i", SCI0_RESFILE_GET_FILE(buf+2)); + tempfh = sci_open(filename, O_RDONLY | O_BINARY); + + if (tempfh == SCI_INVALID_FD) { + files_ok = 0; + break; + } + + close(tempfh); + } + } + + lseek(fh, 0, SEEK_SET); + + return files_ok; +} + +static int +sci_res_read_entry(resource_mgr_t *mgr, resource_source_t *map, + byte *buf, resource_t *res, int sci_version) +{ + res->id = buf[0] | (buf[1] << 8); + res->type = SCI0_RESID_GET_TYPE(buf); + res->number = SCI0_RESID_GET_NUMBER(buf); + res->status = SCI_STATUS_NOMALLOC; + + if (sci_version == SCI_VERSION_01_VGA_ODD) { + res->source = scir_get_volume(mgr, map, SCI01V_RESFILE_GET_FILE(buf + 2)); + res->file_offset = SCI01V_RESFILE_GET_OFFSET(buf + 2); + +#if 0 + if (res->type < 0 || res->type > sci1_last_resource) + return 1; +#endif + } else { + res->source = scir_get_volume(mgr, map, SCI0_RESFILE_GET_FILE(buf + 2)); + res->file_offset = SCI0_RESFILE_GET_OFFSET(buf + 2); + +#if 0 + if (res->type < 0 || res->type > sci0_last_resource) + return 1; +#endif + } + +#if 0 + fprintf(stderr, "Read [%04x] %6d.%s\tresource.%03d, %08x\n", + res->id, res->number, + sci_resource_type_suffixes[res->type], + res->file, res->file_offset); +#endif + + if (res->source == NULL) return 1; + return 0; +} + +inline int sci1_res_type(int ofs, int *types, int lastrt) +{ + int i, last = -1; + + for (i=0;i<=sci1_last_resource;i++) + if (types[i]) + { + if (types[i]>ofs) + return last; + last=i; + } + + return lastrt; +} + +int sci1_parse_header(int fd, int *types, int *lastrt) +{ + unsigned char rtype; + unsigned char offset[2]; + int read_ok; + int size = 0; + + do + { + read_ok = read(fd, &rtype, 1); + if (!read_ok) break; + read_ok = read(fd, &offset, 2); + if (read_ok<2) + read_ok=0; + if (rtype!=0xff) + { + types[rtype&0x7f]=(offset[1]<<8)|(offset[0]); + *lastrt = rtype&0x7f; + } + size+=3; + } while (read_ok && (rtype != 0xFF)); + + if (!read_ok) return 0; + + return size; +} + + + +int +sci0_read_resource_map(resource_mgr_t *mgr, resource_source_t *map, resource_t **resource_p, int *resource_nr_p, int *sci_version) +{ + int fsize; + int fd; + resource_t *resources; + int resources_nr; + int resource_index = 0; + int resources_total_read = 0; + int next_entry; + int max_resfile_nr = 0; + + byte buf[SCI0_RESMAP_ENTRIES_SIZE]; + fd = sci_open(map->location.file.name, O_RDONLY | O_BINARY); + + if (!IS_VALID_FD(fd)) + return SCI_ERROR_RESMAP_NOT_FOUND; + + read(fd, &buf, 4); + + /* Theory: An SCI1 map file begins with an index that allows us to seek quickly + to a particular resource type. The entries are three bytes long; one byte + resource type, two bytes start position and so on. + The below code therefore tests for three things: + + Is the first resource type 'view'? + Do those entries start at an offset that is an exact multiple of the + index entry size? + Is the second resource type 'pic'? + + This requires that a given game has both views and pics, + a safe assumption usually, except in message.map and room-specific + (audio) map files, neither of which SCI0 has. + + */ + + if ((buf[0] == 0x80) && + (buf[1] % 3 == 0) && + (buf[3] == 0x81)) + { + close(fd); + return SCI_ERROR_INVALID_RESMAP_ENTRY; + } + + lseek(fd, 0, SEEK_SET); + + switch (detect_odd_sci01(fd)) + { + case 0 : /* Odd SCI01 */ + if (*sci_version == SCI_VERSION_AUTODETECT) + *sci_version = SCI_VERSION_01_VGA_ODD; + break; + case 1 : /* SCI0 or normal SCI01 */ + if (*sci_version == SCI_VERSION_AUTODETECT) + *sci_version = SCI_VERSION_0; + break; + default : /* Neither, or error occurred */ + return SCI_ERROR_RESMAP_NOT_FOUND; + } + + if ((fsize = sci_fd_size(fd)) < 0) { + perror("Error occured while trying to get filesize of resource.map"); + return SCI_ERROR_RESMAP_NOT_FOUND; + } + + resources_nr = fsize / SCI0_RESMAP_ENTRIES_SIZE; + + resources = (resource_t*)sci_calloc(resources_nr, sizeof(resource_t)); + /* Sets valid default values for most entries */ + + do { + int read_ok = read(fd, &buf, SCI0_RESMAP_ENTRIES_SIZE); + next_entry = 1; + + if (read_ok < 0 ) { + sciprintf("Error while reading %s: ", map->location.file.name); + perror(""); + next_entry = 0; + } else if (read_ok != SCI0_RESMAP_ENTRIES_SIZE) { + next_entry = 0; + } else if (buf[5] == 0xff) /* Most significant offset byte */ + next_entry = 0; + + if (next_entry) { + int fresh = 1; + int addto = resource_index; + int i; + + if (sci_res_read_entry(mgr, map, buf, resources + resource_index, *sci_version)) { + sci_free(resources); + close(fd); + return SCI_ERROR_RESMAP_NOT_FOUND; + } + + for (i = 0; i < resource_index; i++) + if (resources[resource_index].id == + resources[i].id) { + addto = i; + fresh = 0; + } + + _scir_add_altsource(resources + addto, + resources[resource_index].source, + resources[resource_index].file_offset); + + if (fresh) + ++resource_index; + + if (++resources_total_read >= resources_nr) { + sciprintf("Warning: After %d entries, resource.map" + " is not terminated!\n", resource_index); + next_entry = 0; + } + + } + + } while (next_entry); + + close(fd); + + if (!resource_index) { + sciprintf("resource.map was empty!\n"); + _scir_free_resources(resources, resources_nr); + return SCI_ERROR_RESMAP_NOT_FOUND; + } + + if (max_resfile_nr > 999) { + _scir_free_resources(resources, resources_nr); + return SCI_ERROR_INVALID_RESMAP_ENTRY; + } else { +#if 0 +/* Check disabled, Mac SQ3 thinks it has resource.004 but doesn't need it -- CR */ + /* Check whether the highest resfile used exists */ + char filename_buf[14]; + sprintf(filename_buf, "resource.%03d", max_resfile_nr); + fd = sci_open(filename_buf, O_RDONLY); + + if (!IS_VALID_FD(fd)) { + _scir_free_resources(resources, resources_nr); + sciprintf("'%s' requested by resource.map, but not found\n", filename_buf); + return SCI_ERROR_INVALID_RESMAP_ENTRY; + } else + close(fd); +#endif + } + + if (resource_index < resources_nr) + resources = (resource_t*)sci_realloc(resources, sizeof(resource_t) * resource_index); + + *resource_p = resources; + *resource_nr_p = resource_index; + + return 0; +} + +#define TEST fprintf(stderr, "OK in line %d\n", __LINE__); + +static int sci10_or_11(int *types) +{ + int this_restype = 0; + int next_restype = 1; + + while (next_restype <= sci_heap) + { + int could_be_10 = 0; + int could_be_11 = 0; + + while (types[this_restype] == 0) + { + this_restype++; + next_restype++; + } + + while (types[next_restype] == 0) + next_restype++; + + could_be_10 = ((types[next_restype] - types[this_restype]) + % SCI1_RESMAP_ENTRIES_SIZE) == 0; + could_be_11 = ((types[next_restype] - types[this_restype]) + % SCI11_RESMAP_ENTRIES_SIZE) == 0; + + if (could_be_10 && !could_be_11) return SCI_VERSION_1; + if (could_be_11 && !could_be_10) return SCI_VERSION_1_1; + + this_restype++; + next_restype++; + } + + return SCI_VERSION_AUTODETECT; +} + +int +sci1_read_resource_map(resource_mgr_t *mgr, resource_source_t *map, resource_source_t *vol, + resource_t **resource_p, int *resource_nr_p, int *sci_version) +{ + int fsize; + int fd; + resource_t *resources, *resource_start; + int resources_nr; + int resource_index = 0; + int ofs, header_size; + int *types = (int*)sci_malloc(sizeof(int) * (sci1_last_resource+1)); + int i; + byte buf[SCI1_RESMAP_ENTRIES_SIZE]; + int lastrt; + int entrysize; + int entry_size_selector; + + fd = sci_open(map->location.file.name, O_RDONLY | O_BINARY); + + if (!IS_VALID_FD(fd)) + return SCI_ERROR_RESMAP_NOT_FOUND; + + memset(types, 0, sizeof(int) * (sci1_last_resource + 1)); + + if (!(sci1_parse_header(fd, types, &lastrt))) + { + close(fd); + return SCI_ERROR_INVALID_RESMAP_ENTRY; + } + + entry_size_selector = sci10_or_11(types); + if (*sci_version == SCI_VERSION_AUTODETECT) + *sci_version = entry_size_selector; + + if (*sci_version == SCI_VERSION_AUTODETECT) /* That didn't help */ + { + sciprintf("Unable to detect resource map version\n"); + close(fd); + return SCI_ERROR_NO_RESOURCE_FILES_FOUND; + } + + entrysize = entry_size_selector == SCI_VERSION_1_1 + ? SCI11_RESMAP_ENTRIES_SIZE + : SCI1_RESMAP_ENTRIES_SIZE; + + if ((fsize = sci_fd_size(fd)) < 0) { + perror("Error occured while trying to get filesize of resource.map"); + close(fd); + return SCI_ERROR_RESMAP_NOT_FOUND; + } + + resources_nr = (fsize - types[0]) / entrysize; + resource_start = resources = (resource_t*)sci_realloc(mgr->resources, (mgr->resources_nr + resources_nr)*sizeof(resource_t)); + resources += mgr->resources_nr; + + i = 0; + while (types[i] == 0) i++; + header_size = ofs = types[i]; + + lseek(fd, ofs, SEEK_SET); + + for (i=0; ilocation.file.name); + perror(""); + } else read_ok = 1; + break; +#endif + } + + res = &(resources[resource_index]); + res->type = sci1_res_type(ofs, types, lastrt); + res->number= SCI1_RESFILE_GET_NUMBER(buf); + res->status = SCI_STATUS_NOMALLOC; + + if (entry_size_selector < SCI_VERSION_1_1) + { + res->source = scir_get_volume(mgr, map, SCI1_RESFILE_GET_FILE(buf)); + res->file_offset = SCI1_RESFILE_GET_OFFSET(buf); + } else + { + res->source = vol; + res->file_offset = SCI11_RESFILE_GET_OFFSET(buf); + }; + + res->id = res->number | (res->type << 16); + + for (j = 0; i < resource_index; i++) + if (resources[resource_index].id == + resources[i].id) { + addto = i; + fresh = 0; + } + +#if 0 + fprintf(stderr, "Read [%04x] %6d.%s\tresource.%03d, %08x ==> %d\n", + res->id, res->number, + sci_resource_type_suffixes[res->type], + res->file, res->file_offset, addto); +#endif + + _scir_add_altsource(resources + addto, + resources[resource_index].source, + resources[resource_index].file_offset); + + if (fresh) + ++resource_index; + + ofs += entrysize; + } + + close(fd); + free(types); + + *resource_p = resource_start; + *resource_nr_p += resource_index; + return 0; + +} + +#ifdef TEST_RESOURCE_MAP +int +main(int argc, char **argv) +{ + int resources_nr; + resource_t *resources; + int notok = sci0_read_resource_map(".", &resources, &resources_nr); + + if (notok) { + fprintf(stderr,"Failed: Error code %d\n",notok); + return 1; + } + + if (resources) { + int i; + + printf("Found %d resources:\n", resources_nr); + + for (i = 0; i < resources_nr; i++) { + resource_t *res = resources + i; + + printf("#%04d:\tRESOURCE.%03d:%8d\t%s.%03d\n", + i, res->file, res->file_offset, + sci_resource_types[res->type], + res->number); + } + } else + fprintf(stderr, "Found no resources.\n"); + + return 0; +} +#endif diff --git a/engines/sci/scicore/resource_patch.c b/engines/sci/scicore/resource_patch.c new file mode 100644 index 0000000000..3de384e25b --- /dev/null +++ b/engines/sci/scicore/resource_patch.c @@ -0,0 +1,224 @@ +/*************************************************************************** + resource_patch.c Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + + +#include +#include + + +void +sci0_sprintf_patch_file_name(char *string, resource_t *res) +{ + sprintf(string, "%s.%03i", sci_resource_types[res->type], res->number); +} + +void +sci1_sprintf_patch_file_name(char *string, resource_t *res) +{ + sprintf(string, "%d.%s", res->number, sci_resource_type_suffixes[res->type]); +} + +/* version-agnostic patch application */ +static void +process_patch(resource_source_t *source, + char *entry, int restype, int resnumber, resource_t **resource_p, int *resource_nr_p) +{ + int fsize; + char filename[PATH_MAX]; + + if (restype == sci_invalid_resource) + return; + + printf("Patching \"%s\": ", entry); + + sprintf(filename, "%s%s", source->location.dir.name, entry); + fsize = sci_file_size(filename); + if (fsize < 0) + perror("""__FILE__"": (""__LINE__""): sci_file_size()"); + else { + int file; + guint8 filehdr[2]; + resource_t *newrsc = _scir_find_resource_unsorted(*resource_p, + *resource_nr_p, + restype, + resnumber); + + if (fsize < 3) { + printf("File too small\n"); + return; + } + + file = open(entry, O_RDONLY); + if (!IS_VALID_FD(file)) + perror("""__FILE__"": (""__LINE__""): open()"); + else { + int patch_data_offset; + + read(file, filehdr, 2); + + patch_data_offset = filehdr[1]; + + if ((filehdr[0] & 0x7f) != restype) { + printf("Failed; resource type mismatch\n"); + close(file); + } else if (patch_data_offset + 2 >= fsize) { + printf("Failed; patch starting at offset %d can't be in file of size %d\n", filehdr[1] + 2, fsize); + close(file); + } else { + /* Adjust for file offset */ + fsize -= patch_data_offset; + + /* Prepare destination, if neccessary */ + if (!newrsc) { + /* Completely new resource! */ + ++(*resource_nr_p); + *resource_p = (resource_t*)sci_realloc(*resource_p, + *resource_nr_p + * sizeof(resource_t)); + newrsc = (*resource_p-1) + *resource_nr_p; + newrsc->alt_sources = NULL; + } + + /* Overwrite everything, because we're patching */ + newrsc->size = fsize - 2; + newrsc->id = restype << 11 | resnumber; + newrsc->number = resnumber; + newrsc->status = SCI_STATUS_NOMALLOC; + newrsc->type = restype; + newrsc->source = source; + newrsc->file_offset = 2 + patch_data_offset; + + _scir_add_altsource(newrsc, source, 2); + + close(file); + + printf("OK\n"); + + } + } + } +} + + +int +sci0_read_resource_patches(resource_source_t *source, resource_t **resource_p, int *resource_nr_p) +{ + sci_dir_t dir; + char *entry; + char *caller_cwd = sci_getcwd(); + + chdir(source->location.dir.name); + sci_init_dir(&dir); + entry = sci_find_first(&dir, "*.???"); + while (entry) { + int restype = sci_invalid_resource; + int resnumber = -1; + int i; + unsigned int resname_len; + char *endptr; + + for (i = sci_view; i < sci_invalid_resource; i++) + if (strncasecmp(sci_resource_types[i], entry, + strlen(sci_resource_types[i])) == 0) + restype = i; + + if (restype != sci_invalid_resource) { + + resname_len = strlen(sci_resource_types[restype]); + if (entry[resname_len] != '.') + restype = sci_invalid_resource; + else { + resnumber = strtol(entry + 1 + resname_len, + &endptr, 10); /* Get resource number */ + if ((*endptr != '\0') || (resname_len+1 == strlen(entry))) + restype = sci_invalid_resource; + + if ((resnumber < 0) || (resnumber > 1000)) + restype = sci_invalid_resource; + } + } + + process_patch (source, entry, restype, resnumber, resource_p, resource_nr_p); + + entry = sci_find_next(&dir); + } + + chdir(caller_cwd); + free(caller_cwd); + return 0; +} + +int +sci1_read_resource_patches(resource_source_t *source, resource_t **resource_p, int *resource_nr_p) +{ + sci_dir_t dir; + char *entry; + char *caller_cwd = sci_getcwd(); + + chdir(source->location.dir.name); + sci_init_dir(&dir); + entry = sci_find_first(&dir, "*.*"); + while (entry) { + int restype = sci_invalid_resource; + int resnumber = -1; + int i; + char *endptr; + char *dot = strchr(entry, '.'); + + for (i = sci_view; i < sci_invalid_resource; i++) { + if (dot != NULL) { + if (strncasecmp(sci_resource_type_suffixes[i], dot+1, 3) == 0) { + restype = i; + } + } + } + + if (restype != sci_invalid_resource) { + + resnumber = strtol(entry, + &endptr, 10); /* Get resource number */ + + if (endptr != dot) + restype = sci_invalid_resource; + + if (*(dot + 4) != '\0') + restype = sci_invalid_resource; + + if ((resnumber < 0) || (resnumber > 8192)) + restype = sci_invalid_resource; + } + + process_patch (source, entry, restype, resnumber, resource_p, resource_nr_p); + + entry = sci_find_next(&dir); + } + + chdir(caller_cwd); + free(caller_cwd); + return 0; +} + diff --git a/engines/sci/scicore/resourcecheck.c b/engines/sci/scicore/resourcecheck.c new file mode 100644 index 0000000000..f4c731b01f --- /dev/null +++ b/engines/sci/scicore/resourcecheck.c @@ -0,0 +1,38 @@ +/*************************************************************************** + resourcecheck.c (C) 1999 Christoph Reichenbach, TU Darmstadt + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + + History: + + 990403 - created (CJR) + +***************************************************************************/ + +/* #include "resource.h" */ + +int main() +{ + /* No checks yet... */ + return 0; +} diff --git a/engines/sci/scicore/sci_dos.c b/engines/sci/scicore/sci_dos.c new file mode 100644 index 0000000000..0435afa32b --- /dev/null +++ b/engines/sci/scicore/sci_dos.c @@ -0,0 +1,219 @@ +/*************************************************************************** + sci_dos.c Copyright (C) 1999 Rink Springer + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Rink Springer [rink@springer.cx] + +***************************************************************************/ + +#include +#include +#include + +#define G_VA_COPY(ap1, ap2) ((ap1) = (ap2)) + +gpointer +malloc0(guint32 size) { + char* ptr; + + /* allocate the buffer, return NULL if no buffer */ + if((ptr= sci_malloc(size))==NULL) return NULL; + + /* clear it to zeros */ + memset(ptr,0,size); + + /* return the pointer */ + return ptr; +} + +guint +g_printf_string_upper_bound (const gchar* format, + va_list args) +{ + guint len = 1; + + while (*format) + { + gboolean long_int = FALSE; + gboolean extra_long = FALSE; + gchar c; + + c = *format++; + + if (c == '%') + { + gboolean done = FALSE; + + while (*format && !done) + { + switch (*format++) + { + gchar *string_arg; + + case '*': + len += va_arg (args, int); + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* add specified format length, since it might exceed the + * size we assume it to have. + */ + format -= 1; + len += strtol (format, (char**) &format, 10); + break; + case 'h': + /* ignore short int flag, since all args have at least the + * same size as an int + */ + break; + case 'l': + if (long_int) + extra_long = TRUE; /* linux specific */ + else + long_int = TRUE; + break; + case 'q': + case 'L': + long_int = TRUE; + extra_long = TRUE; + break; + case 's': + string_arg = va_arg (args, char *); + if (string_arg) + len += strlen (string_arg); + else + { + /* add enough padding to hold "(null)" identifier */ + len += 16; + } + done = TRUE; + break; + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + { + if (long_int) + (void) va_arg (args, long); + else + (void) va_arg (args, int); + } + len += extra_long ? 64 : 32; + done = TRUE; + break; + case 'D': + case 'O': + case 'U': + (void) va_arg (args, long); + len += 32; + done = TRUE; + break; + case 'e': + case 'E': + case 'f': + case 'g': + (void) va_arg (args, double); + len += extra_long ? 64 : 32; + done = TRUE; + break; + case 'c': + (void) va_arg (args, int); + len += 1; + done = TRUE; + break; + case 'p': + case 'n': + (void) va_arg (args, void*); + len += 32; + done = TRUE; + break; + case '%': + len += 1; + done = TRUE; + break; + default: + /* ignore unknow/invalid flags */ + break; + } + } + } + else + len += 1; + } + +return len; +} + +gchar* +g_strdup_vprintf (const gchar *format, + va_list args1) { + gchar *buffer; + va_list args2; + + G_VA_COPY (args2, args1); + + buffer = g_new (gchar, g_printf_string_upper_bound (format, args1)); + + vsprintf (buffer, format, args2); + va_end (args2); + + return buffer; +} + +gint +g_vsnprintf (gchar *str, + gulong n, + gchar const *fmt, + va_list args) { + gchar* printed; + + + printed = g_strdup_vprintf (fmt, args); + strncpy (str, printed, n); + str[n-1] = '\0'; + + free (printed); + + return strlen (str); +} + +gpointer +g_memdup (gpointer mem, guint byte_size) { + gpointer new_mem; + + if (mem) { + new_mem = sci_malloc (byte_size); + memcpy (new_mem, mem, byte_size); + } else { + new_mem = NULL; + } + + return new_mem; +} diff --git a/engines/sci/scicore/sci_memory.c b/engines/sci/scicore/sci_memory.c new file mode 100644 index 0000000000..ba666e1ceb --- /dev/null +++ b/engines/sci/scicore/sci_memory.c @@ -0,0 +1,311 @@ +/*************************************************************************** + sci_memory.c Copyright (C) 2001 Alexander R Angas + Refcounted memory by Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Alexander R Angas (Alex Angas) + + History: + + 20010815 - assembled from the various memory allocation functions lying + about, namely console.c (extra dmalloc stuff), menubar.c + (for malloc_cpy -> strdup, malloc_ncpy -> strndup). + -- Alex Angas + +***************************************************************************/ + +#include + +/*#define POISON_MEMORY*/ + +/* set optimisations for Win32: */ +/* g on: enable global optimizations */ +/* t on: use fast code */ +/* y on: suppress creation of frame pointers on stack */ +/* s off: disable minimize size code */ + +#ifdef _MSC_VER +# include +# ifndef SATISFY_PURIFY +# pragma optimize( "s", off ) +# pragma optimize( "gty", on ) +# pragma intrinsic( memcpy, strlen ) +# endif +#endif + + +void * +_SCI_MALLOC(size_t size, const char *file, int line, const char *funct) +{ + void *res; +#ifdef MALLOC_DEBUG + INFO_MEMORY("_SCI_MALLOC()", size, file, line, funct); +#endif + ALLOC_MEM((res = malloc(size)), size, file, line, funct) +#ifdef POISON_MEMORY + { + memset(res, 0xa5, size); + } +#endif + return res; +} + + +void * +_SCI_CALLOC(size_t num, size_t size, const char *file, int line, const char *funct) +{ + void *res; +#ifdef MALLOC_DEBUG + INFO_MEMORY("_SCI_CALLOC()", size, file, line, funct); +#endif + ALLOC_MEM((res = calloc(num, size)), num * size, file, line, funct) + return res; +} + + +void * +_SCI_REALLOC(void *ptr, size_t size, const char *file, int line, const char *funct) +{ + void *res; +#ifdef MALLOC_DEBUG + INFO_MEMORY("_SCI_REALLOC()", size, file, line, funct); +#endif + ALLOC_MEM((res = realloc(ptr, size)), size, file, line, funct) + return res; +} + + +void +_SCI_FREE(void *ptr, const char *file, int line, const char *funct) +{ +#ifdef MALLOC_DEBUG + INFO_MEMORY("_SCI_FREE()", 0, file, line, funct); +#endif + if (!ptr) + { + fprintf(stderr, "_SCI_FREE() [%s (%s) : %u]\n", + file, funct, line); + fprintf(stderr, " attempt to free NULL pointer\n"); + BREAKPOINT(); + } + free(ptr); +} + + +void * +_SCI_MEMDUP(const void *ptr, size_t size, const char *file, int line, const char *funct) +{ + void *res; +#ifdef MALLOC_DEBUG + INFO_MEMORY("_SCI_MEMDUP()", size, file, line, funct); +#endif + if (!ptr) + { + fprintf(stderr, "_SCI_MEMDUP() [%s (%s) : %u]\n", + file, funct, line); + fprintf(stderr, " attempt to memdup NULL pointer\n"); + BREAKPOINT(); + } + ALLOC_MEM((res = malloc(size)), size, file, line, funct) + memcpy(res, ptr, size); + return res; +} + + +char * +_SCI_STRDUP(const char *src, const char *file, int line, const char *funct) +{ + void *res; +#ifdef MALLOC_DEBUG + INFO_MEMORY("_SCI_STRDUP()", 0, file, line, funct); +#endif + if (!src) + { + fprintf(stderr, "_SCI_STRDUP() [%s (%s) : %u]\n", + file, funct, line); + fprintf(stderr, " attempt to strdup NULL pointer\n"); + BREAKPOINT(); + } + ALLOC_MEM((res = strdup(src)), strlen(src), file, line, funct) + return (char*)res; +} + + +char * +_SCI_STRNDUP(const char *src, size_t length, const char *file, int line, const char *funct) +{ + void *res; + char *strres; + size_t rlen = (int)MIN(strlen(src), length) + 1; +#ifdef MALLOC_DEBUG + INFO_MEMORY("_SCI_STRNDUP()", 0, file, line, funct); +#endif + if (!src) + { + fprintf(stderr, "_SCI_STRNDUP() [%s (%s) : %u]\n", + file, funct, line); + fprintf(stderr, " attempt to strndup NULL pointer\n"); + BREAKPOINT(); + } + ALLOC_MEM((res = malloc(rlen)), rlen, file, line, funct) + + strres = (char*)res; + strncpy(strres, src, rlen); + strres[rlen - 1] = 0; + + return strres; +} + + +/********** Win32 functions **********/ + +#ifdef _MSC_VER +void +debug_win32_memory(int dbg_setting) +{ +#if defined(NDEBUG) + fprintf(stderr, + "WARNING: Cannot debug Win32 memory in release mode.\n"); +#elif defined(SATISFY_PURIFY) + fprintf(stderr, + "WARNING: Cannot debug Win32 memory in this mode.\n"); +#else + + int tmpFlag; + + tmpFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + + if (dbg_setting > 0) + tmpFlag |= _CRTDBG_CHECK_ALWAYS_DF; + /* call _CrtCheckMemory at every request */ + + if (dbg_setting > 1) + tmpFlag |= _CRTDBG_LEAK_CHECK_DF; + /* perform automatic leak checking at program exit */ + + if (dbg_setting > 2) + tmpFlag |= _CRTDBG_DELAY_FREE_MEM_DF; + /* enable debug heap allocations */ + + if (dbg_setting > 3) + { + PANIC((stderr, "Invalid value for debug_win32_memory!\n")); + BREAKPOINT(); + } + + if (dbg_setting <= 0) + { + /* turn off above */ + tmpFlag &= ~_CRTDBG_CHECK_ALWAYS_DF; + tmpFlag &= ~_CRTDBG_DELAY_FREE_MEM_DF; + tmpFlag &= ~_CRTDBG_LEAK_CHECK_DF; + } + + /* set new state for flag */ + _CrtSetDbgFlag( tmpFlag ); +#endif +} +#endif + + + +/*-------- Refcounting ----------*/ + +#define REFCOUNT_OVERHEAD (sizeof(guint32)*3) +#define REFCOUNT_MAGIC_LIVE_1 0xebdc1741 +#define REFCOUNT_MAGIC_LIVE_2 0x17015ac9 +#define REFCOUNT_MAGIC_DEAD_1 0x11dead11 +#define REFCOUNT_MAGIC_DEAD_2 0x22dead22 + +#define REFCOUNT_CHECK(p) ((((guint32 *)(p))[-3] == REFCOUNT_MAGIC_LIVE_2) \ + && (((guint32 *)(p))[-1] == REFCOUNT_MAGIC_LIVE_1)) + +#define REFCOUNT(p) (((guint32 *)p)[-2]) + +#undef TRACE_REFCOUNT + + +extern void * +sci_refcount_alloc(size_t length) +{ + guint32 *data = (guint32*)sci_malloc(REFCOUNT_OVERHEAD + length); +#ifdef TRACE_REFCOUNT +fprintf(stderr, "[] REF: Real-alloc at %p\n", data); +#endif + data += 3; + + data[-1] = REFCOUNT_MAGIC_LIVE_1; + data[-3] = REFCOUNT_MAGIC_LIVE_2; + REFCOUNT(data) = 1; +#ifdef TRACE_REFCOUNT +fprintf(stderr, "[] REF: Alloc'd %p (ref=%d) OK=%d\n", data, REFCOUNT(data), + REFCOUNT_CHECK(data)); +#endif + return data; +} + +extern void * +sci_refcount_incref(void *data) +{ + if (!REFCOUNT_CHECK(data)) { + BREAKPOINT(); + } else + REFCOUNT(data)++; + +#ifdef TRACE_REFCOUNT +fprintf(stderr, "[] REF: Inc'ing %p (now ref=%d)\n", data, REFCOUNT(data)); +#endif + return data; +} + +extern void +sci_refcount_decref(void *data) +{ +#ifdef TRACE_REFCOUNT +fprintf(stderr, "[] REF: Dec'ing %p (prev ref=%d) OK=%d\n", data, REFCOUNT(data), + REFCOUNT_CHECK(data)); +#endif + if (!REFCOUNT_CHECK(data)) { + BREAKPOINT(); + } else if (--REFCOUNT(data) == 0) { + guint32 *fdata = (guint32*)data; + + fdata[-1] = REFCOUNT_MAGIC_DEAD_1; + fdata[-3] = REFCOUNT_MAGIC_DEAD_2; + +#ifdef TRACE_REFCOUNT +fprintf(stderr, "[] REF: Freeing (%p)...\n", fdata - 3); +#endif + sci_free(fdata - 3); +#ifdef TRACE_REFCOUNT +fprintf(stderr, "[] REF: Done.\n"); +#endif + } +} + +extern void * +sci_refcount_memdup(void *data, size_t len) +{ + void *dest = sci_refcount_alloc(len); + memcpy(dest, data, len); + return dest; +} diff --git a/engines/sci/scicore/script.c b/engines/sci/scicore/script.c new file mode 100644 index 0000000000..921e0fa50b --- /dev/null +++ b/engines/sci/scicore/script.c @@ -0,0 +1,488 @@ +/*************************************************************************** + script.c Copyright (C) 2003 Magnus Reftel, Christoph Reichenbach + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + + Please contact the maintainer for any program-related bug reports or + inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include + +/* #define SCRIPT_DEBUG */ + +#define END Script_None + +opcode_format formats[128][4]={ + /*00*/ + {Script_None}, {Script_None}, {Script_None}, {Script_None}, + /*04*/ + {Script_None}, {Script_None}, {Script_None}, {Script_None}, + /*08*/ + {Script_None}, {Script_None}, {Script_None}, {Script_None}, + /*0C*/ + {Script_None}, {Script_None}, {Script_None}, {Script_None}, + /*10*/ + {Script_None}, {Script_None}, {Script_None}, {Script_None}, + /*14*/ + {Script_None}, {Script_None}, {Script_None}, {Script_SRelative, END}, + /*18*/ + {Script_SRelative, END}, {Script_SRelative, END}, {Script_SVariable, END}, {Script_None}, + /*1C*/ + {Script_SVariable, END}, {Script_None}, {Script_None}, {Script_Variable, END}, + /*20*/ + {Script_SRelative, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_SVariable, Script_Byte, END}, + /*24 (24=ret)*/ + {Script_End}, {Script_Byte, END}, {Script_Invalid}, {Script_Invalid}, + /*28*/ + {Script_Variable, END}, {Script_Invalid}, {Script_Byte, END}, {Script_Variable, Script_Byte, END}, + /*2C*/ + {Script_SVariable, END}, {Script_SVariable, Script_Variable, END}, {Script_None}, {Script_Invalid}, + /*30*/ + {Script_None}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END}, + /*34*/ + {Script_Property, END}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END}, + /*38*/ + {Script_Property, END}, {Script_SRelative, END}, {Script_SRelative, END}, {Script_None}, + /*3C*/ + {Script_None}, {Script_None}, {Script_None}, {Script_Invalid}, + /*40-4F*/ + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + /*50-5F*/ + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + /*60-6F*/ + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + /*70-7F*/ + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END} +}; +#undef END + +void script_adjust_opcode_formats(int res_version) +{ + switch (res_version) + { + case SCI_VERSION_0: + case SCI_VERSION_01: + break; + case SCI_VERSION_01_VGA: + case SCI_VERSION_01_VGA_ODD: + case SCI_VERSION_1_EARLY: + case SCI_VERSION_1_LATE: + formats[op_lofsa][0]=Script_Offset; + formats[op_lofss][0]=Script_Offset; + break; + default: + sciprintf("script_adjust_opcode_formats(): Unknown script version %d\n", res_version); + } +} + +int +script_find_selector(state_t *s, const char *selectorname) +{ + int i; + for (i = 0; i < s->selector_names_nr; i++) + if (strcmp(selectorname, s->selector_names[i]) == 0) + return i; + + sciprintf("Warning: Could not map '%s' to any selector!\n", selectorname); + return -1; +} + +#define FIND_SELECTOR(_slc_, _slcstr_) map->_slc_ = script_find_selector(s, _slcstr_); + +void +script_map_selectors(state_t *s, selector_map_t *map) +{ + map->init = script_find_selector(s, "init"); + map->play = script_find_selector(s, "play"); + FIND_SELECTOR(replay, "replay"); + map->x = script_find_selector(s, "x"); + map->y = script_find_selector(s, "y"); + map->z = script_find_selector(s, "z"); + map->priority = script_find_selector(s, "priority"); + map->view = script_find_selector(s, "view"); + map->loop = script_find_selector(s, "loop"); + map->cel = script_find_selector(s, "cel"); + FIND_SELECTOR(brLeft, "brLeft"); + FIND_SELECTOR(brRight, "brRight"); + FIND_SELECTOR(brTop, "brTop"); + FIND_SELECTOR(brBottom, "brBottom"); + FIND_SELECTOR(xStep, "xStep"); + FIND_SELECTOR(yStep, "yStep"); + FIND_SELECTOR(nsBottom, "nsBottom"); + FIND_SELECTOR(nsTop, "nsTop"); + FIND_SELECTOR(nsLeft, "nsLeft"); + FIND_SELECTOR(nsRight, "nsRight"); + FIND_SELECTOR(font, "font"); + FIND_SELECTOR(text, "text"); + FIND_SELECTOR(type, "type"); + FIND_SELECTOR(state, "state"); + FIND_SELECTOR(doit, "doit"); + FIND_SELECTOR(delete, "delete"); + FIND_SELECTOR(signal, "signal"); + FIND_SELECTOR(underBits, "underBits"); + FIND_SELECTOR(canBeHere, "canBeHere"); + FIND_SELECTOR(client, "client"); + FIND_SELECTOR(dx, "dx"); + FIND_SELECTOR(dy, "dy"); + FIND_SELECTOR(xStep, "xStep"); + FIND_SELECTOR(yStep, "yStep"); + FIND_SELECTOR(b_movCnt, "b-moveCnt"); + FIND_SELECTOR(b_i1, "b-i1"); + FIND_SELECTOR(b_i2, "b-i2"); + FIND_SELECTOR(b_di, "b-di"); + FIND_SELECTOR(b_xAxis, "b-xAxis"); + FIND_SELECTOR(b_incr, "b-incr"); + FIND_SELECTOR(completed, "completed"); + FIND_SELECTOR(illegalBits, "illegalBits"); + FIND_SELECTOR(dispose, "dispose"); + FIND_SELECTOR(prevSignal, "prevSignal"); + FIND_SELECTOR(message, "message"); + FIND_SELECTOR(modifiers, "modifiers"); + FIND_SELECTOR(cue, "cue"); + FIND_SELECTOR(owner, "owner"); + FIND_SELECTOR(handle, "handle"); + FIND_SELECTOR(number, "number"); + FIND_SELECTOR(max, "max"); + FIND_SELECTOR(cursor, "cursor"); + FIND_SELECTOR(claimed, "claimed"); + FIND_SELECTOR(edgeHit, "edgeHit"); + FIND_SELECTOR(wordFail, "wordFail"); + FIND_SELECTOR(syntaxFail, "syntaxFail"); + FIND_SELECTOR(semanticFail, "semanticFail"); + FIND_SELECTOR(cycler, "cycler"); + FIND_SELECTOR(elements, "elements"); + FIND_SELECTOR(lsTop, "lsTop"); + FIND_SELECTOR(lsBottom, "lsBottom"); + FIND_SELECTOR(lsLeft, "lsLeft"); + FIND_SELECTOR(lsRight, "lsRight"); + FIND_SELECTOR(baseSetter, "baseSetter"); + FIND_SELECTOR(who, "who"); + FIND_SELECTOR(distance, "distance"); + FIND_SELECTOR(mover, "mover"); + FIND_SELECTOR(looper, "looper"); + FIND_SELECTOR(isBlocked, "isBlocked"); + FIND_SELECTOR(heading, "heading"); + FIND_SELECTOR(mode, "mode"); + FIND_SELECTOR(caller, "caller"); + FIND_SELECTOR(moveDone, "moveDone"); + FIND_SELECTOR(vol, "vol"); + FIND_SELECTOR(pri, "pri"); + FIND_SELECTOR(min, "min"); + FIND_SELECTOR(sec, "sec"); + FIND_SELECTOR(frame, "frame"); + FIND_SELECTOR(dataInc, "dataInc"); + FIND_SELECTOR(size, "size"); + FIND_SELECTOR(palette, "palette"); + FIND_SELECTOR(moveSpeed, "moveSpeed"); + FIND_SELECTOR(cantBeHere, "cantBeHere"); + FIND_SELECTOR(nodePtr, "nodePtr"); + FIND_SELECTOR(flags, "flags"); + FIND_SELECTOR(points, "points"); +} + +int +sci_hexdump(byte *data, int length, int offsetplus) +{ + char tempstr[40]; + int i; + for (i = 0; i < length; i+= 8) { + int j; + + sprintf(tempstr, "%04x: ", i + offsetplus); + for (j = 0; j < MIN(8, length - i); j++) + sprintf(tempstr + 6 + (j*3) + (j > 3), "%02x ", data[i+j]); + for (j = 0; j < MIN(8, length - i); j++) { + int thechar; + thechar = data[i+j]; + sprintf(tempstr + 31 + j, "%c", + ((thechar < ' ') || (thechar > 127))? '.' : thechar ); + } + + for (j = 0; j < 38; j++) + if (!tempstr[j]) + tempstr[j] = ' '; /* get rid of sprintf's \0s */ + + sciprintf("%s\n", tempstr); + } + return 0; +} + +static void +script_dump_object(char *data, int seeker, int objsize, char **snames, int snames_nr) +{ + int selectors, overloads, selectorsize; + int species = getInt16((unsigned char *) data + 8 + seeker); + int superclass = getInt16((unsigned char *) data + 10 + seeker); + int namepos = getInt16((unsigned char *) data + 14 + seeker); + int i = 0; + + sciprintf("Object\n"); + + sci_hexdump((unsigned char *) data + seeker, objsize -4, seeker); + /*-4 because the size includes the two-word header */ + + sciprintf("Name: %s\n", namepos? ((char *)(data + namepos)) : ""); + sciprintf("Superclass: %x\n", superclass); + sciprintf("Species: %x\n", species); + sciprintf("-info-:%x\n", getInt16((unsigned char *) data + 12 + seeker) & 0xffff); + + sciprintf("Function area offset: %x\n", getInt16((unsigned char *) data + seeker + 4)); + sciprintf("Selectors [%x]:\n", + selectors = (selectorsize = getInt16((unsigned char *) data + seeker + 6))); + + seeker += 8; + + while (selectors--) { + sciprintf(" [#%03x] = 0x%x\n", i++, getInt16((unsigned char *) data + seeker) & 0xffff); + + seeker += 2; + } + + + sciprintf("Overridden functions: %x\n", selectors = + overloads = getInt16((unsigned char *) data + seeker)); + + seeker += 2; + + if (overloads < 100) + while (overloads--) { + int selector = getInt16((unsigned char *) data + (seeker)); + + sciprintf(" [%03x] %s: @", selector & 0xffff, + (snames && selector >= 0 && selector < snames_nr)? snames[selector] : ""); + sciprintf("%04x\n", getInt16((unsigned char *) data + seeker + selectors*2 + 2) & 0xffff); + + seeker += 2; + } +} + +static void +script_dump_class(char *data, int seeker, int objsize, char **snames, int snames_nr) +{ + int selectors, overloads, selectorsize; + int species = getInt16((unsigned char *) data + 8 + seeker); + int superclass = getInt16((unsigned char *) data + 10 + seeker); + int namepos = getInt16((unsigned char *) data + 14 + seeker); + + sciprintf("Class\n"); + + sci_hexdump((unsigned char *) data + seeker, objsize -4, seeker); + + sciprintf("Name: %s\n", namepos? ((char *)data + namepos) : ""); + sciprintf("Superclass: %x\n", superclass); + sciprintf("Species: %x\n", species); + sciprintf("-info-:%x\n", getInt16((unsigned char *) data + 12 + seeker) & 0xffff); + + sciprintf("Function area offset: %x\n", getInt16((unsigned char *) data + seeker + 4)); + sciprintf("Selectors [%x]:\n", + selectors = (selectorsize = getInt16((unsigned char *) data + seeker + 6))); + + seeker += 8; + selectorsize <<= 1; + + while (selectors--) { + int selector = getInt16((unsigned char *) data + (seeker) + selectorsize); + + sciprintf(" [%03x] %s = 0x%x\n", 0xffff & selector, + (snames && selector >= 0 && selector < snames_nr)? snames[selector] : "", + getInt16((unsigned char *) data + seeker) & 0xffff); + + seeker += 2; + } + + seeker += selectorsize; + + sciprintf("Overloaded functions: %x\n", selectors = + overloads = getInt16((unsigned char *) data + seeker)); + + seeker += 2; + + while (overloads--) { + int selector = getInt16((unsigned char *) data + (seeker)); + fprintf(stderr,"selector=%d; snames_nr =%d\n", selector, snames_nr); + sciprintf(" [%03x] %s: @", selector & 0xffff, + (snames && selector >= 0 && selector < snames_nr)? + snames[selector] : ""); + sciprintf("%04x\n", getInt16((unsigned char *) data + seeker + selectors*2 + 2) & 0xffff); + + seeker += 2; + } +} + + +void +script_dissect(resource_mgr_t *resmgr, int res_no, char **snames, int snames_nr) +{ + int objectctr[11] = {0,0,0,0,0,0,0,0,0,0,0}; + unsigned int _seeker = 0; + resource_t *script = scir_find_resource(resmgr, sci_script, res_no, 0); + word_t **words; + int word_count; + + if (!script) { + sciprintf("Script not found!\n"); + return; + } + + words=vocab_get_words (resmgr, &word_count); + + while (_seeker < script->size) { + int objtype = getInt16(script->data + _seeker); + int objsize; + int seeker = _seeker + 4; + + if (!objtype) { + sciprintf("End of script object (#0) encountered.\n"); + sciprintf("Classes: %i, Objects: %i, Export: %i,\n Var: %i (all base 10)", + objectctr[6], objectctr[1], objectctr[7], objectctr[10]); + /*vocabulary_free_snames(snames);*/ + vocab_free_words (words, word_count); + return; + } + + sciprintf("\n"); + + objsize = getInt16(script->data + _seeker + 2); + + sciprintf("Obj type #%x, size 0x%x: ", objtype, objsize); + + _seeker += objsize; + + objectctr[objtype]++; + + switch (objtype) { + case sci_obj_object: + script_dump_object ((char *) script->data, seeker, objsize, snames, snames_nr); + break; + + case sci_obj_code: { + sciprintf("Code\n"); + sci_hexdump(script->data + seeker, objsize -4, seeker); + }; + break; + + case 3: { + sciprintf("\n"); + sci_hexdump(script->data + seeker, objsize -4, seeker); + }; + break; + + case sci_obj_said: { + sciprintf("Said\n"); + sci_hexdump(script->data + seeker, objsize -4, seeker); + + sciprintf ("%04x: ", seeker); + while (seeker < _seeker) + { + unsigned char nextitem=script->data [seeker++]; + if (nextitem == 0xFF) + sciprintf ("\n%04x: ", seeker); + else if (nextitem >= 0xF0) + { + switch (nextitem) + { + case 0xf0: sciprintf(", "); break; + case 0xf1: sciprintf("& "); break; + case 0xf2: sciprintf("/ "); break; + case 0xf3: sciprintf("( "); break; + case 0xf4: sciprintf(") "); break; + case 0xf5: sciprintf("[ "); break; + case 0xf6: sciprintf("] "); break; + case 0xf7: sciprintf("# "); break; + case 0xf8: sciprintf("< "); break; + case 0xf9: sciprintf("> "); break; + } + } + else { + nextitem = nextitem << 8 | script->data [seeker++]; + sciprintf ("%s[%03x] ", vocab_get_any_group_word (nextitem, words, word_count), nextitem); + } + } + sciprintf ("\n"); + } + break; + + case sci_obj_strings: { + sciprintf("Strings\n"); + while (script->data [seeker]) + { + sciprintf ("%04x: %s\n", seeker, script->data+seeker); + seeker += strlen ((char *) script->data+seeker)+1; + } + seeker++; /* the ending zero byte */ + }; + break; + + case sci_obj_class: + script_dump_class ((char *) script->data, seeker, objsize, snames, snames_nr); + break; + + case sci_obj_exports: { + sciprintf("Exports\n"); + sci_hexdump((unsigned char *) script->data + seeker, objsize -4, seeker); + }; + break; + + case sci_obj_pointers: { + sciprintf("Pointers\n"); + sci_hexdump(script->data + seeker, objsize -4, seeker); + }; + break; + + case 9: { + sciprintf("\n"); + sci_hexdump(script->data + seeker, objsize -4, seeker); + }; + break; + + case sci_obj_localvars: { + sciprintf("Local vars\n"); + sci_hexdump(script->data + seeker, objsize -4, seeker); + }; + break; + + default: + sciprintf("Unsupported!\n"); + return; + } + + } + + sciprintf("Script ends without terminator\n"); + + /*vocabulary_free_snames(snames);*/ +} diff --git a/engines/sci/scicore/tools.c b/engines/sci/scicore/tools.c new file mode 100644 index 0000000000..4194a44047 --- /dev/null +++ b/engines/sci/scicore/tools.c @@ -0,0 +1,780 @@ +/*************************************************************************** + tools.c Copyright (C) 1999,2000,2001,2002 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + +#define _GNU_SOURCE /* For FNM_CASEFOLD in fnmatch.h */ + +#include +#include + +#ifdef HAVE_SYS_TIME_H +# include +#endif +#ifdef _MSC_VER +# include +# include +# include +# include +# include +#else +#ifdef _WIN32 +# include +# include +# include +#endif +#endif +#if !defined(HAVE_FNMATCH) && !defined(_MSC_VER) +# include +#endif + +#ifdef _DREAMCAST +# include +#endif + +#ifdef __BEOS__ +# include +#endif + +#ifdef HAVE_MEMFROB +void *memfrob(void *s, size_t n); +#endif + +int script_debug_flag = 0; /* Defaulting to running mode */ +int sci_debug_flags = 0; /* Special flags */ + +#ifndef con_file +# define con_file 0 +#endif + +#define MEMTEST_HARDNESS 31 + +int +memtest(const char *file, int line) +{ + /* va_list argp; -- unused */ + int i; + void *blocks[MEMTEST_HARDNESS + 1]; + fprintf(stderr,"Memtesting in %s, L%d\n", file, line); + + for (i = 0; i < MEMTEST_HARDNESS; i++) { + blocks[i] = sci_malloc(1 + i); +#ifdef HAVE_MEMFROB + memfrob(blocks[i], 1 + i); +#else + memset(blocks[i], 42, 1 + i); +#endif + } + for (i = 0; i < MEMTEST_HARDNESS; i++) + free(blocks[i]); + + for (i = 0; i < MEMTEST_HARDNESS; i++) { + blocks[i] = sci_malloc(5 + i*5); +#ifdef HAVE_MEMFROB + memfrob(blocks[i], 5 + i*5); +#else + memset(blocks[i], 42, 5 + i*5); +#endif + } + for (i = 0; i < MEMTEST_HARDNESS; i++) + free(blocks[i]); + + for (i = 0; i < MEMTEST_HARDNESS; i++) { + blocks[i] = sci_malloc(5 + i*100); +#ifdef HAVE_MEMFROB + memfrob(blocks[i], 5 + i*100); +#else + memset(blocks[i], 42, 5 + i*100); +#endif + } + for (i = 0; i < MEMTEST_HARDNESS; i++) + free(blocks[i]); + + for (i = 0; i < MEMTEST_HARDNESS; i++) { + blocks[i] = sci_malloc(5 + i*1000); +#ifdef HAVE_MEMFROB + memfrob(blocks[i], 5 + i * 1000); +#else + memset(blocks[i], 42, 5 + i * 1000); +#endif + } + for (i = 0; i < MEMTEST_HARDNESS; i++) + free(blocks[i]); + fprintf(stderr,"Memtest succeeded!\n"); + return 0; +} + +void * +memdup(void *src, int size) +{ + void *b = malloc(size); + memcpy(b, src, size); + return b; +} + +int sci_ffs(int _mask) +{ + int retval = 0; + + if (!_mask) return 0; + retval++; + while (! (_mask & 1)) + { + retval++; + _mask >>= 1; + } + + return retval; +} + + +/******************** Debug functions ********************/ + +void +_SCIkvprintf(FILE *file, const char *format, va_list args) +{ + vfprintf(file, format, args); + if (con_file) vfprintf(con_file, format, args); +} + +void +_SCIkprintf(FILE *file, const char *format, ...) +{ + va_list args; + + va_start(args, format); + _SCIkvprintf(file, format, args); + va_end (args); +} + + +void +_SCIkwarn(state_t *s, const char *file, int line, int area, const char *format, ...) +{ + va_list args; + + if (area == SCIkERROR_NR) + _SCIkprintf(stderr, "ERROR: "); + else + _SCIkprintf(stderr, "Warning: "); + + va_start(args, format); + _SCIkvprintf(stderr, format, args); + va_end(args); + fflush(NULL); + + if (sci_debug_flags & _DEBUG_FLAG_BREAK_ON_WARNINGS) script_debug_flag=1; +} + +void +_SCIkdebug(state_t *s, const char *file, int line, int area, const char *format, ...) +{ + va_list args; + + if (s->debug_mode & (1 << area)) { + _SCIkprintf(stdout, " kernel: (%s L%d): ", file, line); + va_start(args, format); + _SCIkvprintf(stdout, format, args); + va_end(args); + fflush(NULL); + } +} + +void +_SCIGNUkdebug(const char *funcname, state_t *s, const char *file, int line, int area, const char *format, ...) +{ + va_list xargs; + int error = ((area == SCIkWARNING_NR) || (area == SCIkERROR_NR)); + + if (error || (s->debug_mode & (1 << area))) { /* Is debugging enabled for this area? */ + + _SCIkprintf(stderr, "FSCI: "); + + if (area == SCIkERROR_NR) + _SCIkprintf(stderr, "ERROR in %s ", funcname); + else if (area == SCIkWARNING_NR) + _SCIkprintf(stderr, "%s: Warning ", funcname); + else _SCIkprintf(stderr, funcname); + + _SCIkprintf(stderr, "(%s L%d): ", file, line); + + va_start(xargs, format); + _SCIkvprintf(stderr, format, xargs); + va_end(xargs); + + } +} + + +#if defined(HAVE_GETTIMEOFDAY) +void +sci_gettime(long *seconds, long *useconds) +{ + struct timeval tv; + + assert(!gettimeofday(&tv, NULL)); + *seconds = tv.tv_sec; + *useconds = tv.tv_usec; +} +#elif defined (_WIN32) + +/*WARNING(Incorrect)*/ +/* Warning: This function only retrieves the amount of mseconds since the start of +** the Win32 kernel; it does /not/ provide the number of seconds since the epoch! +** There are no known cases where this causes problems, though. */ +void sci_gettime(long *seconds, long *useconds) +{ + DWORD tm; + + if (TIMERR_NOERROR != timeBeginPeriod(1)) + { + fprintf(stderr, "timeBeginPeriod(1) failed in sci_gettime\n"); + } + + tm = timeGetTime(); + + if (TIMERR_NOERROR != timeEndPeriod(1)) + { + fprintf(stderr, "timeEndPeriod(1) failed in sci_gettime\n"); + } + + *seconds = tm/1000; + *useconds = (tm%1000)*1000; +} +#else +# error "You need to provide a microsecond resolution sci_gettime implementation for your platform!" +#endif + + +void +sci_get_current_time(GTimeVal *val) +{ + long foo, bar; + sci_gettime(&foo, &bar); + val->tv_sec = foo; + val->tv_usec = bar; +} + + +/************* Directory entities *************/ +#if defined(_WIN32) && defined(_MSC_VER) +/******** Dir: Win32 CODE ********/ + +void +sci_init_dir(sci_dir_t *dir) +{ + dir->search = -1; +} + +char * +sci_find_first(sci_dir_t *dir, const char *mask) +{ + dir->search = _findfirst(mask, &(dir->fileinfo)); + + if (dir->search != -1) + { + if (dir->fileinfo.name == NULL) + { + return NULL; + } + + if (strcmp(dir->fileinfo.name, ".") == 0 || + strcmp(dir->fileinfo.name, "..") == 0) + { + if (sci_find_next(dir) == NULL) + { + return NULL; + } + } + + return dir->fileinfo.name; + } + else + { + switch (errno) + { + case ENOENT: + { +#ifdef _DEBUG + printf("_findfirst errno = ENOENT: no match\n"); + + if (mask) + printf(" in: %s\n", mask); + else + printf(" - searching in undefined directory\n"); +#endif + break; + } + case EINVAL: + { + printf("_findfirst errno = EINVAL: invalid filename\n"); + break; + } + default: + printf("_findfirst errno = unknown (%d)", errno); + } + } + + return NULL; +} + +char * +sci_find_next(sci_dir_t *dir) +{ + if (dir->search == -1) + return NULL; + + if (_findnext(dir->search, &(dir->fileinfo)) < 0) { + _findclose(dir->search); + dir->search = -1; + return NULL; + } + + if (strcmp(dir->fileinfo.name, ".") == 0 || + strcmp(dir->fileinfo.name, "..") == 0) + { + if (sci_find_next(dir) == NULL) + { + return NULL; + } + } + + return dir->fileinfo.name; +} + +void +sci_finish_find(sci_dir_t *dir) +{ + if(dir->search != -1) { + _findclose(dir->search); + dir->search = -1; + } +} + +#else /* !_WIN32 */ +/******** Dir: UNIX CODE ********/ + +void +sci_init_dir(sci_dir_t *dir) +{ + dir->dir = NULL; + dir->mask_copy = NULL; +} + +char * +sci_find_first(sci_dir_t *dir, const char *mask) +{ + if (dir->dir) + closedir(dir->dir); + + if (!(dir->dir = opendir("."))) { + sciprintf("%s, L%d: opendir(\".\") failed!\n", __FILE__, __LINE__); + return NULL; + } + + dir->mask_copy = sci_strdup(mask); + + return sci_find_next(dir); +} + +#ifndef FNM_CASEFOLD +#define FNM_CASEFOLD 0 +#warning "File searches will not be case-insensitive!" +#endif + +char * +sci_find_next(sci_dir_t *dir) +{ + struct dirent *match; + + while ((match = readdir(dir->dir))) { + if (match->d_name[0] == '.') + continue; + + if (!fnmatch(dir->mask_copy, match->d_name, FNM_CASEFOLD)) + return match->d_name; + } + + sci_finish_find(dir); + return NULL; +} + +void +sci_finish_find(sci_dir_t *dir) +{ + if (dir->dir) { + closedir(dir->dir); + dir->dir = NULL; + free(dir->mask_copy); + dir->mask_copy = NULL; + } +} + +#endif /* !_WIN32 */ + +/************* /Directory entities *************/ + + +int +sci_mkpath(const char *path) +{ + const char *path_position = path; + char *next_separator = NULL; + + if (chdir(G_DIR_SEPARATOR_S)) { /* Go to root */ + sciprintf("Error: Could not change to root directory '%s'!\n", + G_DIR_SEPARATOR_S); + return -1; + } + + do { + if (next_separator) + *next_separator = G_DIR_SEPARATOR_S[0]; + next_separator = (char *)strchr(path_position, G_DIR_SEPARATOR_S[0]); + + if (next_separator) + *next_separator = 0; + + if (*path_position) { /* Unless we're at the first slash... */ + if (chdir(path_position)) { + if (scimkdir(path_position, 0700) || chdir(path_position)) { + sciprintf("Error: Could not create subdirectory '%s' in", + path_position); + if (next_separator) + *next_separator = G_DIR_SEPARATOR_S[0]; + sciprintf(" '%s'!\n", path); + return -2; + } + } + } + path_position = next_separator + 1; + + } while (next_separator); + + return 0; +} + + + +char * +sci_get_homedir(void) +{ +#ifdef _WIN32 + char *_path_buf = (char*)malloc(MAX_PATH); + char *dr = getenv("HOMEDRIVE"); + char *path = getenv("HOMEPATH"); + + if (!dr || !path) + return getenv("WINDIR"); + + strncpy(_path_buf, dr, 4); + strncat(_path_buf, path, MAX_PATH - 4); + + return _path_buf; +#elif defined(__unix__) || !defined(X_DISPLAY_MISSING) || defined (__BEOS__) || defined(MACOSX) + return getenv("HOME"); +#elif defined(_DREAMCAST) + return NULL; +#elif defined(__amigaos4__) + return "/PROGDIR/"; +#else +# error Please add a $HOME policy for your platform! +#endif +} + + +sci_queue_t * +sci_init_queue(sci_queue_t *queue) +{ + queue->start = queue->end = NULL; + return queue; +} + +sci_queue_t * +sci_add_to_queue(sci_queue_t *queue, void *data, int type) +{ + sci_queue_node_t *node = (sci_queue_node_t*)sci_malloc(sizeof(sci_queue_node_t)); + + node->next = NULL; + node->data = data; + node->type = type; + + if (queue->start) + queue->start->next = node; + + queue->start = node; + + if (!queue->end) + queue->end = node; + + return queue; +} + +void * +sci_get_from_queue(sci_queue_t *queue, int *type) +{ + sci_queue_node_t *node = queue->end; + if (node) { + void *retval = node->data; + if (type) + *type = node->type; + + queue->end = node->next; + + if (queue->end == NULL) /* Queue empty? */ + queue->start = NULL; + + free(node); + return retval; + } + return NULL; +} + + +/*-- Yielding to the scheduler --*/ + +#ifdef HAVE_SCHED_YIELD +# include + +void +sci_sched_yield(void) +{ + sched_yield(); +} + +#elif defined (_DREAMCAST) + +void +sci_sched_yield() +{ + thd_pass(); +} + +#elif defined (__BEOS__) + +void +sci_sched_yield() +{ + snooze(0); +} + +#elif defined (_WIN32) + +void +sci_sched_yield() +{ + sleep(1); +} + +#else + +void +sci_sched_yield() +{ +} + +#endif /* !HAVE_SCHED_YIELD */ + + +char * +_fcaseseek(const char *fname, sci_dir_t *dir) +/* Expects *dir to be uninitialized and the caller to + ** free it afterwards */ +{ + char *buf, *iterator; + char _buf[14]; + char *retval = NULL, *name; + +#ifdef _MSC_VER + return fname; +#endif + + if (strchr(fname, G_DIR_SEPARATOR)) { + fprintf(stderr, "_fcaseseek() does not support subdirs\n"); + BREAKPOINT(); + } + + if (strlen(fname) > 12) /* not a DOS file? */ + buf = (char*)sci_malloc(strlen(fname) + 1); + else + buf = _buf; + + sci_init_dir(dir); + + /* Replace all letters with '?' chars */ + strcpy(buf, fname); + iterator = buf; + while (*iterator) { + if (isalpha(*iterator)) + *iterator = '?'; + iterator++; + } + + name = sci_find_first(dir, buf); + + while (name && !retval) { + if (!strcasecmp(fname, name)) + retval = name; + else + name = sci_find_next(dir); + } + + if (strlen(fname) > 12) + free(buf); + + return retval; +} + + +FILE * +sci_fopen(const char *fname, const char *mode) +{ + sci_dir_t dir; + char *name = _fcaseseek(fname, &dir); + FILE *file = NULL; + + if (name) + file = fopen(name, mode); + else if (strchr(mode, 'w')) + file = fopen(fname, mode); + + sci_finish_find(&dir); /* Free memory */ + + return file; +} + +int +sci_open(const char *fname, int flags) +{ + sci_dir_t dir; + char *name; + int file = SCI_INVALID_FD; + char *separator_position; + char *path; + char *caller_cwd; + + sci_init_dir(&dir); + + separator_position = (char *)strrchr(fname, G_DIR_SEPARATOR); + if (separator_position) + { + path = (char *) malloc(separator_position-fname+1); + path[separator_position-fname] = 0; + strncpy(path, fname, separator_position-fname); + chdir(path); + free(path); + } + + name = _fcaseseek(separator_position ? separator_position + 1 : fname, &dir); + if (name) + file = open(name, flags); + + sci_finish_find(&dir); /* Free memory */ + + caller_cwd = sci_getcwd(); + chdir(caller_cwd); + free(caller_cwd); + + return file; +} + +char * +sci_getcwd(void) +{ + int size = 0; + char *cwd = NULL; + + while (size < 8192) { + size += 256; + cwd = (char*)sci_malloc(size); + if (getcwd(cwd, size-1)) + return cwd; + + sci_free(cwd); + } + + fprintf(stderr,"Could not determine current working directory!\n"); + return NULL; +} + +#ifdef _DREAMCAST + +int +sci_fd_size(int fd) +{ + return fs_total(fd); +} + +int +sci_file_size(const char *fname) +{ + int fd = fs_open(fname, O_RDONLY); + int retval = -1; + + if (fd != 0) { + retval = sci_fd_size(fd); + fs_close(fd); + } + + return retval; +} + +#else + +int +sci_fd_size(int fd) +{ + struct stat fd_stat; + if (fstat(fd, &fd_stat)) return -1; + return fd_stat.st_size; +} + +int +sci_file_size(const char *fname) +{ + struct stat fn_stat; + if (stat(fname, &fn_stat)) return -1; + return fn_stat.st_size; +} + +#endif + +/* Simple heuristic to work around array handling peculiarity in SQ4: +It uses StrAt() to read the individual elements, so we must determine +whether a string is really a string or an array. */ +int is_print_str(char *str) +{ + int printable = 0; + int len = strlen(str); + + if (len == 0) return 1; + + while (*str) + { + if (isprint(*str)) printable++; + str++; + } + + return ((float) printable / (float) len >= 0.5); +} diff --git a/engines/sci/scicore/treedef.1 b/engines/sci/scicore/treedef.1 new file mode 100644 index 0000000000..496046bb60 --- /dev/null +++ b/engines/sci/scicore/treedef.1 @@ -0,0 +1,31 @@ + BRANCH_NODE(0, 1, 2) + BRANCH_NODE(1, 3, 4) + BRANCH_NODE(2, 5, 6) + BRANCH_NODE(3, 7, 8) + BRANCH_NODE(4, 9, 10) + BRANCH_NODE(5, 11, 12) + LEAF_NODE (6, 1) + BRANCH_NODE(7, 13, 14) + BRANCH_NODE(8, 15, 16) + BRANCH_NODE(9, 17, 18) + LEAF_NODE (10, 3) + LEAF_NODE (11, 2) + LEAF_NODE (12, 0) + BRANCH_NODE(13, 19, 20) + BRANCH_NODE(14, 21, 22) + BRANCH_NODE(15, 23, 24) + LEAF_NODE (16, 6) + LEAF_NODE (17, 5) + LEAF_NODE (18, 4) + BRANCH_NODE(19, 25, 26) + BRANCH_NODE(20, 27, 28) + LEAF_NODE (21, 10) + LEAF_NODE (22, 9) + LEAF_NODE (23, 8) + LEAF_NODE (24, 7) + BRANCH_NODE(25, 29, 30) + LEAF_NODE (26, 13) + LEAF_NODE (27, 12) + LEAF_NODE (28, 11) + LEAF_NODE (29, 15) + LEAF_NODE (30, 14) diff --git a/engines/sci/scicore/treedef.2 b/engines/sci/scicore/treedef.2 new file mode 100644 index 0000000000..b4d71bdfed --- /dev/null +++ b/engines/sci/scicore/treedef.2 @@ -0,0 +1,127 @@ + BRANCH_NODE(0, 1, 2) + BRANCH_NODE(1, 3, 4) + BRANCH_NODE(2, 5, 6) + BRANCH_NODE(3, 7, 8) + BRANCH_NODE(4, 9, 10) + BRANCH_NODE(5, 11, 12) + LEAF_NODE (6, 0) + BRANCH_NODE(7, 13, 14) + BRANCH_NODE(8, 15, 16) + BRANCH_NODE(9, 17, 18) + BRANCH_NODE(10, 19, 20) + BRANCH_NODE(11, 21, 22) + BRANCH_NODE(12, 23, 24) + BRANCH_NODE(13, 25, 26) + BRANCH_NODE(14, 27, 28) + BRANCH_NODE(15, 29, 30) + BRANCH_NODE(16, 31, 32) + BRANCH_NODE(17, 33, 34) + BRANCH_NODE(18, 35, 36) + BRANCH_NODE(19, 37, 38) + BRANCH_NODE(20, 39, 40) + BRANCH_NODE(21, 41, 42) + BRANCH_NODE(22, 43, 44) + LEAF_NODE (23, 2) + LEAF_NODE (24, 1) + BRANCH_NODE(25, 45, 46) + BRANCH_NODE(26, 47, 48) + BRANCH_NODE(27, 49, 50) + BRANCH_NODE(28, 51, 52) + BRANCH_NODE(29, 53, 54) + BRANCH_NODE(30, 55, 56) + BRANCH_NODE(31, 57, 58) + BRANCH_NODE(32, 59, 60) + BRANCH_NODE(33, 61, 62) + BRANCH_NODE(34, 63, 64) + BRANCH_NODE(35, 65, 66) + BRANCH_NODE(36, 67, 68) + BRANCH_NODE(37, 69, 70) + BRANCH_NODE(38, 71, 72) + BRANCH_NODE(39, 73, 74) + BRANCH_NODE(40, 75, 76) + LEAF_NODE (41, 6) + LEAF_NODE (42, 5) + LEAF_NODE (43, 4) + LEAF_NODE (44, 3) + BRANCH_NODE(45, 77, 78) + BRANCH_NODE(46, 79, 80) + BRANCH_NODE(47, 81, 82) + BRANCH_NODE(48, 83, 84) + BRANCH_NODE(49, 85, 86) + BRANCH_NODE(50, 87, 88) + BRANCH_NODE(51, 89, 90) + BRANCH_NODE(52, 91, 92) + BRANCH_NODE(53, 93, 94) + BRANCH_NODE(54, 95, 96) + BRANCH_NODE(55, 97, 98) + BRANCH_NODE(56, 99, 100) + BRANCH_NODE(57, 101, 102) + BRANCH_NODE(58, 103, 104) + BRANCH_NODE(59, 105, 106) + BRANCH_NODE(60, 107, 108) + BRANCH_NODE(61, 109, 110) + LEAF_NODE (62, 21) + LEAF_NODE (63, 20) + LEAF_NODE (64, 19) + LEAF_NODE (65, 18) + LEAF_NODE (66, 17) + LEAF_NODE (67, 16) + LEAF_NODE (68, 15) + LEAF_NODE (69, 14) + LEAF_NODE (70, 13) + LEAF_NODE (71, 12) + LEAF_NODE (72, 11) + LEAF_NODE (73, 10) + LEAF_NODE (74, 9) + LEAF_NODE (75, 8) + LEAF_NODE (76, 7) + BRANCH_NODE(77, 111, 112) + BRANCH_NODE(78, 113, 114) + BRANCH_NODE(79, 115, 116) + BRANCH_NODE(80, 117, 118) + BRANCH_NODE(81, 119, 120) + BRANCH_NODE(82, 121, 122) + BRANCH_NODE(83, 123, 124) + BRANCH_NODE(84, 125, 126) + LEAF_NODE (85, 47) + LEAF_NODE (86, 46) + LEAF_NODE (87, 45) + LEAF_NODE (88, 44) + LEAF_NODE (89, 43) + LEAF_NODE (90, 42) + LEAF_NODE (91, 41) + LEAF_NODE (92, 40) + LEAF_NODE (93, 39) + LEAF_NODE (94, 38) + LEAF_NODE (95, 37) + LEAF_NODE (96, 36) + LEAF_NODE (97, 35) + LEAF_NODE (98, 34) + LEAF_NODE (99, 33) + LEAF_NODE (100, 32) + LEAF_NODE (101, 31) + LEAF_NODE (102, 30) + LEAF_NODE (103, 29) + LEAF_NODE (104, 28) + LEAF_NODE (105, 27) + LEAF_NODE (106, 26) + LEAF_NODE (107, 25) + LEAF_NODE (108, 24) + LEAF_NODE (109, 23) + LEAF_NODE (110, 22) + LEAF_NODE (111, 63) + LEAF_NODE (112, 62) + LEAF_NODE (113, 61) + LEAF_NODE (114, 60) + LEAF_NODE (115, 59) + LEAF_NODE (116, 58) + LEAF_NODE (117, 57) + LEAF_NODE (118, 56) + LEAF_NODE (119, 55) + LEAF_NODE (120, 54) + LEAF_NODE (121, 53) + LEAF_NODE (122, 52) + LEAF_NODE (123, 51) + LEAF_NODE (124, 50) + LEAF_NODE (125, 49) + LEAF_NODE (126, 48) diff --git a/engines/sci/scicore/treedef.3 b/engines/sci/scicore/treedef.3 new file mode 100644 index 0000000000..f75a01c1e6 --- /dev/null +++ b/engines/sci/scicore/treedef.3 @@ -0,0 +1,511 @@ + BRANCH_NODE(0, 1, 2) + BRANCH_NODE(1, 3, 4) + BRANCH_NODE(2, 5, 6) + BRANCH_NODE(3, 7, 8) + BRANCH_NODE(4, 9, 10) + BRANCH_NODE(5, 11, 12) + BRANCH_NODE(6, 13, 14) + BRANCH_NODE(7, 15, 16) + BRANCH_NODE(8, 17, 18) + BRANCH_NODE(9, 19, 20) + BRANCH_NODE(10, 21, 22) + BRANCH_NODE(11, 23, 24) + BRANCH_NODE(12, 25, 26) + BRANCH_NODE(13, 27, 28) + BRANCH_NODE(14, 29, 30) + BRANCH_NODE(15, 31, 32) + BRANCH_NODE(16, 33, 34) + BRANCH_NODE(17, 35, 36) + BRANCH_NODE(18, 37, 38) + BRANCH_NODE(19, 39, 40) + BRANCH_NODE(20, 41, 42) + BRANCH_NODE(21, 43, 44) + BRANCH_NODE(22, 45, 46) + BRANCH_NODE(23, 47, 48) + BRANCH_NODE(24, 49, 50) + BRANCH_NODE(25, 51, 52) + BRANCH_NODE(26, 53, 54) + BRANCH_NODE(27, 55, 56) + BRANCH_NODE(28, 57, 58) + BRANCH_NODE(29, 59, 60) + LEAF_NODE (30, 32) + BRANCH_NODE(31, 61, 62) + BRANCH_NODE(32, 63, 64) + BRANCH_NODE(33, 65, 66) + BRANCH_NODE(34, 67, 68) + BRANCH_NODE(35, 69, 70) + BRANCH_NODE(36, 71, 72) + BRANCH_NODE(37, 73, 74) + BRANCH_NODE(38, 75, 76) + BRANCH_NODE(39, 77, 78) + BRANCH_NODE(40, 79, 80) + BRANCH_NODE(41, 81, 82) + BRANCH_NODE(42, 83, 84) + BRANCH_NODE(43, 85, 86) + BRANCH_NODE(44, 87, 88) + BRANCH_NODE(45, 89, 90) + BRANCH_NODE(46, 91, 92) + BRANCH_NODE(47, 93, 94) + BRANCH_NODE(48, 95, 96) + BRANCH_NODE(49, 97, 98) + LEAF_NODE (50, 117) + LEAF_NODE (51, 116) + LEAF_NODE (52, 115) + LEAF_NODE (53, 114) + LEAF_NODE (54, 111) + LEAF_NODE (55, 110) + LEAF_NODE (56, 108) + LEAF_NODE (57, 105) + LEAF_NODE (58, 101) + LEAF_NODE (59, 97) + LEAF_NODE (60, 69) + BRANCH_NODE(61, 99, 100) + BRANCH_NODE(62, 101, 102) + BRANCH_NODE(63, 103, 104) + BRANCH_NODE(64, 105, 106) + BRANCH_NODE(65, 107, 108) + BRANCH_NODE(66, 109, 110) + BRANCH_NODE(67, 111, 112) + BRANCH_NODE(68, 113, 114) + BRANCH_NODE(69, 115, 116) + BRANCH_NODE(70, 117, 118) + BRANCH_NODE(71, 119, 120) + BRANCH_NODE(72, 121, 122) + BRANCH_NODE(73, 123, 124) + BRANCH_NODE(74, 125, 126) + BRANCH_NODE(75, 127, 128) + BRANCH_NODE(76, 129, 130) + BRANCH_NODE(77, 131, 132) + BRANCH_NODE(78, 133, 134) + LEAF_NODE (79, 112) + LEAF_NODE (80, 109) + LEAF_NODE (81, 104) + LEAF_NODE (82, 103) + LEAF_NODE (83, 102) + LEAF_NODE (84, 100) + LEAF_NODE (85, 99) + LEAF_NODE (86, 98) + LEAF_NODE (87, 84) + LEAF_NODE (88, 83) + LEAF_NODE (89, 82) + LEAF_NODE (90, 79) + LEAF_NODE (91, 78) + LEAF_NODE (92, 76) + LEAF_NODE (93, 73) + LEAF_NODE (94, 68) + LEAF_NODE (95, 67) + LEAF_NODE (96, 65) + LEAF_NODE (97, 49) + LEAF_NODE (98, 45) + BRANCH_NODE(99, 135, 136) + BRANCH_NODE(100, 137, 138) + BRANCH_NODE(101, 139, 140) + BRANCH_NODE(102, 141, 142) + BRANCH_NODE(103, 143, 144) + BRANCH_NODE(104, 145, 146) + BRANCH_NODE(105, 147, 148) + BRANCH_NODE(106, 149, 150) + BRANCH_NODE(107, 151, 152) + BRANCH_NODE(108, 153, 154) + BRANCH_NODE(109, 155, 156) + BRANCH_NODE(110, 157, 158) + BRANCH_NODE(111, 159, 160) + BRANCH_NODE(112, 161, 162) + BRANCH_NODE(113, 163, 164) + LEAF_NODE (114, 119) + LEAF_NODE (115, 107) + LEAF_NODE (116, 85) + LEAF_NODE (117, 80) + LEAF_NODE (118, 77) + LEAF_NODE (119, 70) + LEAF_NODE (120, 66) + LEAF_NODE (121, 61) + LEAF_NODE (122, 56) + LEAF_NODE (123, 55) + LEAF_NODE (124, 53) + LEAF_NODE (125, 52) + LEAF_NODE (126, 51) + LEAF_NODE (127, 50) + LEAF_NODE (128, 48) + LEAF_NODE (129, 46) + LEAF_NODE (130, 44) + LEAF_NODE (131, 41) + LEAF_NODE (132, 40) + LEAF_NODE (133, 13) + LEAF_NODE (134, 10) + BRANCH_NODE(135, 165, 166) + BRANCH_NODE(136, 167, 168) + BRANCH_NODE(137, 169, 170) + BRANCH_NODE(138, 171, 172) + BRANCH_NODE(139, 173, 174) + BRANCH_NODE(140, 175, 176) + BRANCH_NODE(141, 177, 178) + BRANCH_NODE(142, 179, 180) + BRANCH_NODE(143, 181, 182) + BRANCH_NODE(144, 183, 184) + BRANCH_NODE(145, 185, 186) + BRANCH_NODE(146, 187, 188) + BRANCH_NODE(147, 189, 190) + BRANCH_NODE(148, 191, 192) + LEAF_NODE (149, 121) + LEAF_NODE (150, 120) + LEAF_NODE (151, 118) + LEAF_NODE (152, 95) + LEAF_NODE (153, 91) + LEAF_NODE (154, 87) + LEAF_NODE (155, 72) + LEAF_NODE (156, 71) + LEAF_NODE (157, 58) + LEAF_NODE (158, 57) + LEAF_NODE (159, 54) + LEAF_NODE (160, 47) + LEAF_NODE (161, 42) + LEAF_NODE (162, 39) + LEAF_NODE (163, 34) + LEAF_NODE (164, 9) + BRANCH_NODE(165, 193, 194) + BRANCH_NODE(166, 195, 196) + BRANCH_NODE(167, 197, 198) + BRANCH_NODE(168, 199, 200) + BRANCH_NODE(169, 201, 202) + BRANCH_NODE(170, 203, 204) + BRANCH_NODE(171, 205, 206) + BRANCH_NODE(172, 207, 208) + BRANCH_NODE(173, 209, 210) + BRANCH_NODE(174, 211, 212) + BRANCH_NODE(175, 213, 214) + BRANCH_NODE(176, 215, 216) + BRANCH_NODE(177, 217, 218) + BRANCH_NODE(178, 219, 220) + BRANCH_NODE(179, 221, 222) + BRANCH_NODE(180, 223, 224) + BRANCH_NODE(181, 225, 226) + BRANCH_NODE(182, 227, 228) + BRANCH_NODE(183, 229, 230) + BRANCH_NODE(184, 231, 232) + BRANCH_NODE(185, 233, 234) + LEAF_NODE (186, 93) + LEAF_NODE (187, 89) + LEAF_NODE (188, 88) + LEAF_NODE (189, 86) + LEAF_NODE (190, 75) + LEAF_NODE (191, 62) + LEAF_NODE (192, 43) + BRANCH_NODE(193, 235, 236) + BRANCH_NODE(194, 237, 238) + BRANCH_NODE(195, 239, 240) + BRANCH_NODE(196, 241, 242) + BRANCH_NODE(197, 243, 244) + BRANCH_NODE(198, 245, 246) + BRANCH_NODE(199, 247, 248) + BRANCH_NODE(200, 249, 250) + BRANCH_NODE(201, 251, 252) + BRANCH_NODE(202, 253, 254) + BRANCH_NODE(203, 255, 256) + BRANCH_NODE(204, 257, 258) + BRANCH_NODE(205, 259, 260) + BRANCH_NODE(206, 261, 262) + BRANCH_NODE(207, 263, 264) + BRANCH_NODE(208, 265, 266) + BRANCH_NODE(209, 267, 268) + BRANCH_NODE(210, 269, 270) + BRANCH_NODE(211, 271, 272) + BRANCH_NODE(212, 273, 274) + BRANCH_NODE(213, 275, 276) + BRANCH_NODE(214, 277, 278) + BRANCH_NODE(215, 279, 280) + BRANCH_NODE(216, 281, 282) + BRANCH_NODE(217, 283, 284) + BRANCH_NODE(218, 285, 286) + BRANCH_NODE(219, 287, 288) + BRANCH_NODE(220, 289, 290) + BRANCH_NODE(221, 291, 292) + BRANCH_NODE(222, 293, 294) + BRANCH_NODE(223, 295, 296) + BRANCH_NODE(224, 297, 298) + BRANCH_NODE(225, 299, 300) + BRANCH_NODE(226, 301, 302) + BRANCH_NODE(227, 303, 304) + BRANCH_NODE(228, 305, 306) + BRANCH_NODE(229, 307, 308) + LEAF_NODE (230, 122) + LEAF_NODE (231, 113) + LEAF_NODE (232, 38) + LEAF_NODE (233, 36) + LEAF_NODE (234, 33) + BRANCH_NODE(235, 309, 310) + BRANCH_NODE(236, 311, 312) + BRANCH_NODE(237, 313, 314) + BRANCH_NODE(238, 315, 316) + BRANCH_NODE(239, 317, 318) + BRANCH_NODE(240, 319, 320) + BRANCH_NODE(241, 321, 322) + BRANCH_NODE(242, 323, 324) + BRANCH_NODE(243, 325, 326) + BRANCH_NODE(244, 327, 328) + BRANCH_NODE(245, 329, 330) + BRANCH_NODE(246, 331, 332) + BRANCH_NODE(247, 333, 334) + BRANCH_NODE(248, 335, 336) + BRANCH_NODE(249, 337, 338) + BRANCH_NODE(250, 339, 340) + BRANCH_NODE(251, 341, 342) + BRANCH_NODE(252, 343, 344) + BRANCH_NODE(253, 345, 346) + BRANCH_NODE(254, 347, 348) + BRANCH_NODE(255, 349, 350) + BRANCH_NODE(256, 351, 352) + BRANCH_NODE(257, 353, 354) + BRANCH_NODE(258, 355, 356) + BRANCH_NODE(259, 357, 358) + BRANCH_NODE(260, 359, 360) + BRANCH_NODE(261, 361, 362) + BRANCH_NODE(262, 363, 364) + BRANCH_NODE(263, 365, 366) + BRANCH_NODE(264, 367, 368) + BRANCH_NODE(265, 369, 370) + BRANCH_NODE(266, 371, 372) + BRANCH_NODE(267, 373, 374) + BRANCH_NODE(268, 375, 376) + BRANCH_NODE(269, 377, 378) + BRANCH_NODE(270, 379, 380) + BRANCH_NODE(271, 381, 382) + BRANCH_NODE(272, 383, 384) + BRANCH_NODE(273, 385, 386) + BRANCH_NODE(274, 387, 388) + BRANCH_NODE(275, 389, 390) + BRANCH_NODE(276, 391, 392) + BRANCH_NODE(277, 393, 394) + BRANCH_NODE(278, 395, 396) + BRANCH_NODE(279, 397, 398) + BRANCH_NODE(280, 399, 400) + BRANCH_NODE(281, 401, 402) + BRANCH_NODE(282, 403, 404) + BRANCH_NODE(283, 405, 406) + BRANCH_NODE(284, 407, 408) + BRANCH_NODE(285, 409, 410) + BRANCH_NODE(286, 411, 412) + BRANCH_NODE(287, 413, 414) + BRANCH_NODE(288, 415, 416) + BRANCH_NODE(289, 417, 418) + BRANCH_NODE(290, 419, 420) + BRANCH_NODE(291, 421, 422) + BRANCH_NODE(292, 423, 424) + BRANCH_NODE(293, 425, 426) + BRANCH_NODE(294, 427, 428) + BRANCH_NODE(295, 429, 430) + BRANCH_NODE(296, 431, 432) + BRANCH_NODE(297, 433, 434) + BRANCH_NODE(298, 435, 436) + LEAF_NODE (299, 124) + LEAF_NODE (300, 123) + LEAF_NODE (301, 106) + LEAF_NODE (302, 92) + LEAF_NODE (303, 90) + LEAF_NODE (304, 81) + LEAF_NODE (305, 74) + LEAF_NODE (306, 63) + LEAF_NODE (307, 60) + LEAF_NODE (308, 0) + BRANCH_NODE(309, 437, 438) + BRANCH_NODE(310, 439, 440) + BRANCH_NODE(311, 441, 442) + BRANCH_NODE(312, 443, 444) + BRANCH_NODE(313, 445, 446) + BRANCH_NODE(314, 447, 448) + BRANCH_NODE(315, 449, 450) + BRANCH_NODE(316, 451, 452) + BRANCH_NODE(317, 453, 454) + BRANCH_NODE(318, 455, 456) + BRANCH_NODE(319, 457, 458) + BRANCH_NODE(320, 459, 460) + BRANCH_NODE(321, 461, 462) + BRANCH_NODE(322, 463, 464) + BRANCH_NODE(323, 465, 466) + BRANCH_NODE(324, 467, 468) + BRANCH_NODE(325, 469, 470) + BRANCH_NODE(326, 471, 472) + BRANCH_NODE(327, 473, 474) + BRANCH_NODE(328, 475, 476) + BRANCH_NODE(329, 477, 478) + BRANCH_NODE(330, 479, 480) + BRANCH_NODE(331, 481, 482) + BRANCH_NODE(332, 483, 484) + BRANCH_NODE(333, 485, 486) + BRANCH_NODE(334, 487, 488) + BRANCH_NODE(335, 489, 490) + BRANCH_NODE(336, 491, 492) + BRANCH_NODE(337, 493, 494) + BRANCH_NODE(338, 495, 496) + BRANCH_NODE(339, 497, 498) + BRANCH_NODE(340, 499, 500) + BRANCH_NODE(341, 501, 502) + BRANCH_NODE(342, 503, 504) + BRANCH_NODE(343, 505, 506) + BRANCH_NODE(344, 507, 508) + BRANCH_NODE(345, 509, 510) + LEAF_NODE (346, 244) + LEAF_NODE (347, 243) + LEAF_NODE (348, 242) + LEAF_NODE (349, 238) + LEAF_NODE (350, 233) + LEAF_NODE (351, 229) + LEAF_NODE (352, 225) + LEAF_NODE (353, 223) + LEAF_NODE (354, 222) + LEAF_NODE (355, 221) + LEAF_NODE (356, 220) + LEAF_NODE (357, 219) + LEAF_NODE (358, 218) + LEAF_NODE (359, 217) + LEAF_NODE (360, 216) + LEAF_NODE (361, 215) + LEAF_NODE (362, 214) + LEAF_NODE (363, 213) + LEAF_NODE (364, 212) + LEAF_NODE (365, 211) + LEAF_NODE (366, 210) + LEAF_NODE (367, 209) + LEAF_NODE (368, 208) + LEAF_NODE (369, 207) + LEAF_NODE (370, 206) + LEAF_NODE (371, 205) + LEAF_NODE (372, 204) + LEAF_NODE (373, 203) + LEAF_NODE (374, 202) + LEAF_NODE (375, 201) + LEAF_NODE (376, 200) + LEAF_NODE (377, 199) + LEAF_NODE (378, 198) + LEAF_NODE (379, 197) + LEAF_NODE (380, 196) + LEAF_NODE (381, 195) + LEAF_NODE (382, 194) + LEAF_NODE (383, 193) + LEAF_NODE (384, 192) + LEAF_NODE (385, 191) + LEAF_NODE (386, 190) + LEAF_NODE (387, 189) + LEAF_NODE (388, 188) + LEAF_NODE (389, 187) + LEAF_NODE (390, 186) + LEAF_NODE (391, 185) + LEAF_NODE (392, 184) + LEAF_NODE (393, 183) + LEAF_NODE (394, 182) + LEAF_NODE (395, 181) + LEAF_NODE (396, 180) + LEAF_NODE (397, 179) + LEAF_NODE (398, 178) + LEAF_NODE (399, 177) + LEAF_NODE (400, 176) + LEAF_NODE (401, 127) + LEAF_NODE (402, 126) + LEAF_NODE (403, 125) + LEAF_NODE (404, 96) + LEAF_NODE (405, 94) + LEAF_NODE (406, 64) + LEAF_NODE (407, 59) + LEAF_NODE (408, 37) + LEAF_NODE (409, 35) + LEAF_NODE (410, 31) + LEAF_NODE (411, 30) + LEAF_NODE (412, 29) + LEAF_NODE (413, 28) + LEAF_NODE (414, 27) + LEAF_NODE (415, 25) + LEAF_NODE (416, 24) + LEAF_NODE (417, 23) + LEAF_NODE (418, 22) + LEAF_NODE (419, 21) + LEAF_NODE (420, 20) + LEAF_NODE (421, 19) + LEAF_NODE (422, 18) + LEAF_NODE (423, 17) + LEAF_NODE (424, 16) + LEAF_NODE (425, 15) + LEAF_NODE (426, 14) + LEAF_NODE (427, 12) + LEAF_NODE (428, 11) + LEAF_NODE (429, 8) + LEAF_NODE (430, 7) + LEAF_NODE (431, 6) + LEAF_NODE (432, 5) + LEAF_NODE (433, 4) + LEAF_NODE (434, 3) + LEAF_NODE (435, 2) + LEAF_NODE (436, 1) + LEAF_NODE (437, 255) + LEAF_NODE (438, 254) + LEAF_NODE (439, 253) + LEAF_NODE (440, 252) + LEAF_NODE (441, 251) + LEAF_NODE (442, 250) + LEAF_NODE (443, 249) + LEAF_NODE (444, 248) + LEAF_NODE (445, 247) + LEAF_NODE (446, 246) + LEAF_NODE (447, 245) + LEAF_NODE (448, 241) + LEAF_NODE (449, 240) + LEAF_NODE (450, 239) + LEAF_NODE (451, 237) + LEAF_NODE (452, 236) + LEAF_NODE (453, 235) + LEAF_NODE (454, 234) + LEAF_NODE (455, 232) + LEAF_NODE (456, 231) + LEAF_NODE (457, 230) + LEAF_NODE (458, 228) + LEAF_NODE (459, 227) + LEAF_NODE (460, 226) + LEAF_NODE (461, 224) + LEAF_NODE (462, 175) + LEAF_NODE (463, 174) + LEAF_NODE (464, 173) + LEAF_NODE (465, 172) + LEAF_NODE (466, 171) + LEAF_NODE (467, 170) + LEAF_NODE (468, 169) + LEAF_NODE (469, 168) + LEAF_NODE (470, 167) + LEAF_NODE (471, 166) + LEAF_NODE (472, 165) + LEAF_NODE (473, 164) + LEAF_NODE (474, 163) + LEAF_NODE (475, 162) + LEAF_NODE (476, 161) + LEAF_NODE (477, 160) + LEAF_NODE (478, 159) + LEAF_NODE (479, 158) + LEAF_NODE (480, 157) + LEAF_NODE (481, 156) + LEAF_NODE (482, 155) + LEAF_NODE (483, 154) + LEAF_NODE (484, 153) + LEAF_NODE (485, 152) + LEAF_NODE (486, 151) + LEAF_NODE (487, 150) + LEAF_NODE (488, 149) + LEAF_NODE (489, 148) + LEAF_NODE (490, 147) + LEAF_NODE (491, 146) + LEAF_NODE (492, 145) + LEAF_NODE (493, 144) + LEAF_NODE (494, 143) + LEAF_NODE (495, 142) + LEAF_NODE (496, 141) + LEAF_NODE (497, 140) + LEAF_NODE (498, 139) + LEAF_NODE (499, 138) + LEAF_NODE (500, 137) + LEAF_NODE (501, 136) + LEAF_NODE (502, 135) + LEAF_NODE (503, 134) + LEAF_NODE (504, 133) + LEAF_NODE (505, 132) + LEAF_NODE (506, 131) + LEAF_NODE (507, 130) + LEAF_NODE (508, 129) + LEAF_NODE (509, 128) + LEAF_NODE (510, 26) diff --git a/engines/sci/scicore/versions.c b/engines/sci/scicore/versions.c new file mode 100644 index 0000000000..06cab77cd1 --- /dev/null +++ b/engines/sci/scicore/versions.c @@ -0,0 +1,368 @@ +/*************************************************************************** + Copyright (C) 2005 Christoph Reichenbach + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +***************************************************************************/ + +#define NEED_SCI_VERSIONS +#include +#include +#include +#include +#include "games.h" +#include "exe.h" + +/* Maxmimum number of bytes to hash from start of file */ +#define VERSION_DETECT_HASH_SIZE 1000000 + +#define VERSION_DETECT_BUF_SIZE 4096 + +static int +scan_file(char *filename, sci_version_t *version) +{ + char buf[VERSION_DETECT_BUF_SIZE]; + char result_string[10]; /* string-encoded result, copied from buf */ + int characters_left; + int state = 0; + /* 'state' encodes how far we have matched the version pattern + ** "n.nnn.nnn" + ** + ** n.nnn.nnn + ** 0123456789 + ** + ** Since we cannot be certain that the pattern does not begin with an + ** alphanumeric character, some states are ambiguous. + ** The pattern is expected to be terminated with a non-alphanumeric + ** character. + */ + + exe_file_t *f = exe_open(filename); + + if (!f) + return 1; + + do { + int i; + int accept; + + characters_left = exe_read(f, buf, VERSION_DETECT_BUF_SIZE); + + for (i = 0; i < characters_left; i++) { + const char ch = buf[i]; + accept = 0; /* By default, we don't like this character */ + + if (isalnum((unsigned char) ch)) { + accept = (state != 1 + && state != 5 + && state != 9); + } else if (ch == '.') { + accept = (state == 1 + || state == 5); + } else if (state == 9) { + result_string[9] = 0; /* terminate string */ + + if (!version_parse(result_string, version)) + { + exe_close(f); + return 0; /* success! */ + } + + /* Continue searching. */ + } + + if (accept) + result_string[state++] = ch; + else + state = 0; + + } + + } while (characters_left == VERSION_DETECT_BUF_SIZE); + + exe_close(f); + return 1; /* failure */ +} + +static guint32 +read_uint32(byte *data) +{ + return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; +} + +static guint16 +read_uint16(byte *data) +{ + return (data[0] << 8) | data[1]; +} + +static int +is_mac_exe(char *filename) +{ + FILE *file; + byte buf[4]; + guint32 val; + int i; + + /* Mac executables have no extension */ + if (strchr(filename, '.')) + return 0; + + file = fopen(filename, "rb"); + if (!file) + return 0; + + if (fseek(file, 4, SEEK_SET) == -1) { + fclose(file); + return 0; + } + + /* Read resource map offset */ + if (fread(buf, 1, 4, file) < 4) { + fclose(file); + return 0; + } + + val = read_uint32(buf); + + if (fseek(file, val + 28, SEEK_SET) == -1) { + fclose(file); + return 0; + } + + /* Read number of types in map */ + if (fread(buf, 1, 2, file) < 2) { + fclose(file); + return 0; + } + + val = read_uint16(buf) + 1; + + for (i = 0; i < val; i++) { + if (fread(buf, 1, 4, file) < 4) { + fclose(file); + return 0; + } + + /* Look for executable code */ + if (!memcmp(buf, "CODE", 4)) { + fclose(file); + return 1; + } + + /* Skip to next list entry */ + if (fseek(file, 4, SEEK_CUR) == -1) { + fclose(file); + return 0; + } + } + + fclose(file); + return 0; +} + +static int +is_exe(char *filename) +{ + FILE *file; + char buf[4]; + char header[] = {0x00, 0x00, 0x03, 0xf3}; + + /* PC and Atari ST executable extensions */ + if (strstr(filename, ".exe") || strstr(filename, ".EXE") + || strstr(filename, ".prg") || strstr(filename, ".PRG")) + return 1; + + /* Check for Amiga executable */ + if (strchr(filename, '.')) + return 0; + + file = fopen(filename, "rb"); + if (!file) + return 0; + + if (fread(buf, 1, 4, file) < 4) { + fclose(file); + return 0; + } + + fclose(file); + + /* Check header bytes */ + return memcmp(buf, header, 4) == 0; +} + +void +version_require_earlier_than(state_t *s, sci_version_t version) +{ + if (s->version_lock_flag) + return; + + if (version <= s->min_version) { + sciprintf("Version autodetect conflict: Less than %d.%03d.%03d was requested, but " + "%d.%03d.%03d is the current minimum\n", + SCI_VERSION_MAJOR(version), SCI_VERSION_MINOR(version), SCI_VERSION_PATCHLEVEL(version), + SCI_VERSION_MAJOR(s->min_version), SCI_VERSION_MINOR(s->min_version), + SCI_VERSION_PATCHLEVEL(s->min_version)); + return; + } + else if (version < s->max_version) { + s->max_version = version -1; + if (s->max_version < s->version) + s->version = s->max_version; + } +} + +void +version_require_later_than(state_t *s, sci_version_t version) +{ + if (s->version_lock_flag) + return; + + if (version > s->max_version) { + sciprintf("Version autodetect conflict: More than %d.%03d.%03d was requested, but less than" + "%d.%03d.%03d is required ATM\n", + SCI_VERSION_MAJOR(version), SCI_VERSION_MINOR(version), SCI_VERSION_PATCHLEVEL(version), + SCI_VERSION_MAJOR(s->max_version), SCI_VERSION_MINOR(s->max_version), + SCI_VERSION_PATCHLEVEL(s->max_version)); + return; + } + else if (version > s->min_version) { + s->min_version = version; + if (s->min_version > s->version) + s->version = s->min_version; + } +} + +int +version_parse(char *vn, sci_version_t *result) +{ + char *endptr[3]; + int major = strtol(vn, &endptr[0], 10); + int minor = strtol(vn + 2, &endptr[1], 10); + int patchlevel = strtol(vn + 6, &endptr[2], 10); + + if (endptr[0] != vn + 1 || endptr[1] != vn + 5 + || *endptr[2] != '\0') { + sciprintf("Warning: Failed to parse version string '%s'\n", vn); + return 1; + } + + *result = SCI_VERSION(major, minor, patchlevel); + return 0; +} + +int +version_detect_from_executable(sci_version_t *result) +{ + sci_dir_t dir; + char *filename; + int mac = 0; + + /* For Mac versions we need to search the resource fork */ + mac = !chdir(".rsrc"); + + sci_init_dir(&dir); + + filename = sci_find_first(&dir, "*"); + + while (filename) { + if (mac ? is_mac_exe(filename) : is_exe(filename)) + if (!scan_file(filename, result)) { + sci_finish_find(&dir); + + if (mac) + chdir(".."); + + return 0; + } + + filename = sci_find_next(&dir); + } + + if (mac) + chdir(".."); + + return 1; +} + +#define HASHCODE_MAGIC_RESOURCE_000 0x55555555 +#define HASHCODE_MAGIC_RESOURCE_001 0x00000001 + +const char * /* Original version by Solomon Peachy */ +version_guess_from_hashcode(sci_version_t *result, int *res_version, guint32 *code) +{ + int i; + int fd = -1; + int left = VERSION_DETECT_HASH_SIZE; + guint32 hash_code; + guint8 buf[VERSION_DETECT_BUF_SIZE]; + + if (IS_VALID_FD(fd = sci_open("resource.001", O_RDONLY|O_BINARY))) { + hash_code = HASHCODE_MAGIC_RESOURCE_001; + } else if (IS_VALID_FD(fd = sci_open("resource.000", O_RDONLY|O_BINARY))) { + hash_code = HASHCODE_MAGIC_RESOURCE_000; + } else { + sciprintf("Warning: Could not find RESOURCE.000 or RESOURCE.001, cannot determine hash code\n"); + *code = 0; + /* complete and utter failure */ + return NULL; + } + + while (left > 0) { + int len = read(fd, buf, left < VERSION_DETECT_BUF_SIZE ? left : VERSION_DETECT_BUF_SIZE); + + if (len == -1) { + sciprintf("Warning: read error while computing hash code for resource file\n"); + *code = 0; + return NULL; + } + + if (len == 0) + /* EOF */ + break; + + for (i = 0; i < len; i++) + hash_code = (hash_code * 19) + *(buf + i); + + /* This is the string hashing algorithm used by Objective Caml 3.08; the general idea + ** of multiplying the previous hash code with a prime number between 5 and 23 appears + ** to be generally considered to be a "good" approach to exhausting the entire 32 bit + ** number space in a somewhat equal distribution. For large chunks of data, such as + ** SCI resource files, this should both perform well and yield a good distribution, + ** or at least that's what standard library designers have been assuming for quite a + ** while. */ + + left -= len; + } + + close(fd); + + *code = hash_code; + + for (i = 0 ; sci_games[i].name ; i++) { + if (sci_games[i].id == hash_code) { + *result = sci_games[i].version; + *res_version = sci_games[i].res_version; + return sci_games[i].name; + } + } + + return NULL; /* Failed to find matching game */ +} + + +#undef VERSION_DETECT_BUF_SIZE diff --git a/engines/sci/scicore/vocab.c b/engines/sci/scicore/vocab.c new file mode 100644 index 0000000000..dd70cc2fbe --- /dev/null +++ b/engines/sci/scicore/vocab.c @@ -0,0 +1,713 @@ +/*************************************************************************** + vocab.c (C) 1999 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ +/* Main vocabulary support functions and word lookup */ + + +#include +#include +#include + +#include + +int vocab_version; + +#define VOCAB_RESOURCE_PARSE_TREE_BRANCHES vocab_version==1 ? \ + VOCAB_RESOURCE_SCI1_PARSE_TREE_BRANCHES : \ + VOCAB_RESOURCE_SCI0_PARSE_TREE_BRANCHES + +#define VOCAB_RESOURCE_SUFFIX_VOCAB vocab_version==1 ? \ + VOCAB_RESOURCE_SCI1_SUFFIX_VOCAB : \ + VOCAB_RESOURCE_SCI0_SUFFIX_VOCAB + +const char *class_names[] = + {"", /* These strange names were taken from an SCI01 interpreter */ + "", + "conj", /* conjunction */ + "ass", /* ? */ + "pos", /* preposition ? */ + "art", /* article */ + "adj", /* adjective */ + "pron", /* pronoun */ + "noun", /* noun */ + "auxv", /* auxillary verb */ + "adv", /* adverb */ + "verb", /* verb */ + "", + "", + "", + ""}; + + +int +_vocab_cmp_words(const void *word1, const void *word2) +{ + return strcasecmp((*((word_t **) word1))->word, (*((word_t **) word2))->word); +} + + +word_t ** +vocab_get_words(resource_mgr_t *resmgr, int *word_counter) +{ + int counter = 0; + unsigned int seeker; + word_t **words; + + char currentword[256] = ""; /* They're not going to use words longer than 255 ;-) */ + int currentwordpos = 0; + + resource_t *resource; + + /* First try to load the SCI0 vocab resource. */ + resource = scir_find_resource(resmgr, sci_vocab, + VOCAB_RESOURCE_SCI0_MAIN_VOCAB, 0); + vocab_version = 0; + + if (!resource) { + fprintf(stderr,"SCI0: Could not find a main vocabulary, trying SCI01.\n"); + resource = scir_find_resource(resmgr, sci_vocab, + VOCAB_RESOURCE_SCI1_MAIN_VOCAB, 0); + vocab_version = 1; + } + + if (!resource) { + fprintf(stderr,"SCI1: Could not find a main vocabulary!\n"); + return NULL; /* NOT critical: SCI1 games and some demos don't have one! */ + } + + if (vocab_version == 1) + seeker = 255 * 2; /* vocab.900 starts with 255 16-bit pointers which we don't use */ + else + seeker = 26 * 2; /* vocab.000 starts with 26 16-bit pointers which we don't use */ + + if (resource->size < seeker) { + fprintf(stderr, "Invalid main vocabulary encountered: Too small\n"); + return NULL; + /* Now this ought to be critical, but it'll just cause parse() and said() not to work */ + } + + words = (word_t**)sci_malloc(sizeof(word_t *)); + + while (seeker < resource->size) { + byte c; + + words = (word_t**)sci_realloc(words, (counter + 1) * sizeof(word_t *)); + + currentwordpos = resource->data[seeker++]; /* Parts of previous words may be re-used */ + + if (vocab_version == 1) { + c = 1; + while (seeker < resource->size + && currentwordpos < 255 + && c) { + c = resource->data[seeker++]; + currentword[currentwordpos++] = c; + } + if (seeker == resource->size) { + fprintf(stderr, "SCI1: Vocabulary not usable, disabling.\n"); + vocab_free_words(words, counter); + return NULL; + } + } else { + do { + c = resource->data[seeker++]; + currentword[currentwordpos++] = c & 0x7f; /* 0x80 is used to terminate the string */ + } while (c < 0x80); + } + + currentword[currentwordpos] = 0; + + words[counter] = (word_t*)sci_malloc(sizeof(word_t) + currentwordpos); + /* Allocate more memory, so that the word fits into the structure */ + + strcpy(&(words[counter]->word[0]), &(currentword[0])); /* Copy the word */ + + /* Now decode class and group: */ + c = resource->data[seeker + 1]; + words[counter]->w_class = ((resource->data[seeker]) << 4) | ((c & 0xf0) >> 4); + words[counter]->group = (resource->data[seeker + 2]) | ((c & 0x0f) << 8); + + seeker += 3; + ++counter; + + } + + *word_counter = counter; + + qsort(words, counter, sizeof(word_t *), _vocab_cmp_words); /* Sort entries */ + + return words; +} + + +void +vocab_free_words(word_t **words, int words_nr) +{ + int i; + + for (i = 0; i < words_nr; i++) + free(words[i]); + + free(words); +} + + +const char * +vocab_get_any_group_word(int group, word_t **words, int words_nr) +{ + int i; + + if (group == VOCAB_MAGIC_NUMBER_GROUP) + return "{number}"; + + for (i = 0; i < words_nr; i++) + if (words[i]->group == group) + return words[i]->word; + + return "{invalid}"; +} + + +static inline unsigned int +inverse_16(unsigned int foo) +{ + return (((foo & 0xff) << 8) | ((foo & 0xff00) >> 8)); +} + +suffix_t ** +vocab_get_suffices(resource_mgr_t *resmgr, int *suffices_nr) +{ + int counter = 0; + suffix_t **suffices; + resource_t *resource = scir_find_resource(resmgr, sci_vocab, VOCAB_RESOURCE_SUFFIX_VOCAB, 1); + unsigned int seeker = 1; + + if (!resource) { + fprintf(stderr,"Could not find suffix vocabulary!\n"); + return NULL; /* Not critical */ + } + + suffices = (suffix_t**)sci_malloc(sizeof(suffix_t *)); + + while ((seeker < resource->size-1) && (resource->data[seeker + 1] != 0xff)) { + + char *alt_suffix = (char *) resource->data + seeker; + int alt_len = strlen(alt_suffix); + char *word_suffix; + int word_len; + + suffices = (suffix_t**)sci_realloc(suffices, sizeof(suffix_t *) * (counter + 1)); + + seeker += alt_len + 1; /* Hit end of string */ + word_suffix = (char *) resource->data + seeker + 3; /* Beginning of next string +1 (ignore '*') */ + word_len = strlen(word_suffix); + + suffices[counter] = (suffix_t*)sci_malloc(sizeof(suffix_t)); + /* allocate enough memory to store the strings */ + + suffices[counter]->word_suffix = word_suffix; + suffices[counter]->alt_suffix = alt_suffix; + + suffices[counter]->alt_suffix_length = alt_len; + suffices[counter]->word_suffix_length = word_len; + suffices[counter]->class_mask = inverse_16(getInt16(resource->data + seeker)); /* Inverse endianness */ + + seeker += word_len + 4; + suffices[counter]->result_class = inverse_16(getInt16(resource->data + seeker)); + seeker += 3; /* Next entry */ + + ++counter; + + } + + *suffices_nr = counter; + return suffices; +} + + + +void +vocab_free_suffices(resource_mgr_t *resmgr, suffix_t **suffices, int suffices_nr) +{ + int i; + + scir_unlock_resource(resmgr, scir_find_resource(resmgr, sci_vocab, VOCAB_RESOURCE_SUFFIX_VOCAB, 0), + VOCAB_RESOURCE_SUFFIX_VOCAB, sci_vocab); + + for (i = 0; i < suffices_nr; i++) + free(suffices[i]); + + free(suffices); +} + + +void +vocab_free_branches(parse_tree_branch_t *parser_branches) +{ + if (parser_branches) + free(parser_branches); +} + + +parse_tree_branch_t * +vocab_get_branches(resource_mgr_t * resmgr, int *branches_nr) +{ + resource_t *resource = scir_find_resource(resmgr, sci_vocab, + VOCAB_RESOURCE_PARSE_TREE_BRANCHES, 0); + parse_tree_branch_t *retval; + int i; + + if (!resource) { + fprintf(stderr,"No parser tree data found!\n"); + return NULL; + } + + *branches_nr = resource->size / 20; + + if (*branches_nr == 0) { + fprintf(stderr,"Parser tree data is empty!\n"); + return NULL; + } + + retval = (parse_tree_branch_t*)sci_malloc(sizeof(parse_tree_branch_t) * *branches_nr); + + for (i = 0; i < *branches_nr; i++) { + int k; + + byte *base = resource->data + i*20; + + retval[i].id = getInt16(base); + + for (k = 0; k < 9; k++) + retval[i].data[k] = getUInt16(base + 2 + 2*k); + + retval[i].data[9] = 0; /* Always terminate */ + } + + if (!retval[*branches_nr - 1].id) /* branch lists may be terminated by empty rules */ + --(*branches_nr); + + return retval; +} + + +result_word_t * +vocab_lookup_word(char *word, int word_len, + word_t **words, int words_nr, + suffix_t **suffices, int suffices_nr) +{ + word_t *tempword = (word_t*)sci_malloc(sizeof(word_t) + word_len + 256); + /* 256: For suffices. Should suffice. */ + word_t **dict_word; + result_word_t *retval; + char *tester; + int i, word_len_tmp; + + strncpy(&(tempword->word[0]), word, word_len); + tempword->word[word_len] = 0; + + word_len_tmp = word_len; + while ((tester = strchr(tempword->word, '-'))) + memmove(tester, tester + 1, (tempword->word + word_len_tmp--) - tester); + + retval = (result_word_t*)sci_malloc(sizeof(result_word_t)); + + dict_word = (word_t**)bsearch(&tempword, words, words_nr, sizeof(word_t *), _vocab_cmp_words); + + if (dict_word) { + free(tempword); + + retval->w_class = (*dict_word)->w_class; + retval->group = (*dict_word)->group; + + return retval; + } + + /* Now try all suffices */ + for (i = 0; i < suffices_nr; i++) + if (suffices[i]->alt_suffix_length <= word_len) { + + int suff_index = word_len - suffices[i]->alt_suffix_length; + /* Offset of the start of the suffix */ + + if (strncasecmp(suffices[i]->alt_suffix, word + suff_index, + suffices[i]->alt_suffix_length) == 0) { /* Suffix matched! */ + + strncpy(&(tempword->word[0]), word, word_len); + tempword->word[suff_index] = 0; /* Terminate word at suffix start position... */ + strncat(&(tempword->word[0]), suffices[i]->word_suffix, suffices[i]->word_suffix_length); /* ...and append "correct" suffix */ + + dict_word = (word_t**)bsearch(&tempword, words, words_nr, sizeof(word_t *), _vocab_cmp_words); + + if ((dict_word) && ((*dict_word)->w_class & suffices[i]->class_mask)) { /* Found it? */ + free(tempword); + + retval->w_class = suffices[i]->result_class; /* Use suffix class */ + retval->group = (*dict_word)->group; + + return retval; + } + } + } + + /* No match so far? Check if it's a number. */ + + strncpy(&(tempword->word[0]), word, word_len); + tempword->word[word_len] = 0; + + word_len_tmp = word_len; + while ((tester = strchr(tempword->word, '-'))) + memmove(tester, tester + 1, (tempword->word + word_len--) - tester); + + if ((strtol(&(tempword->word[0]), &tester, 10) >= 0) + && (*tester == '\0')) { /* Do we have a complete number here? */ + free(tempword); + + retval->group = VOCAB_MAGIC_NUMBER_GROUP; + retval->w_class = VOCAB_CLASS_NUMBER; + + return(retval); + } + + free(tempword); + free(retval); + return NULL; +} + +int +vocab_get_said_spec_length(byte *addr) +{ + int result = 0; + + while (*addr != 0xff) + { + if (*addr < 0xf0) + { + result += 2; + addr += 2; + } else + { + result += 1; + addr += 1; + } + } + + return result + 1; +} + +void +vocab_decypher_said_block(state_t *s, byte *addr) +{ + int nextitem; + + do { + nextitem = *addr++; + + if (nextitem < 0xf0) { + nextitem = nextitem << 8 | *addr++; + sciprintf(" %s[%03x]", vocab_get_any_group_word(nextitem, s->parser_words, s->parser_words_nr), + nextitem); + + nextitem = 42; /* Make sure that group 0xff doesn't abort */ + } else switch(nextitem) { + case 0xf0: sciprintf(" ,"); break; + case 0xf1: sciprintf(" &"); break; + case 0xf2: sciprintf(" /"); break; + case 0xf3: sciprintf(" ("); break; + case 0xf4: sciprintf(" )"); break; + case 0xf5: sciprintf(" ["); break; + case 0xf6: sciprintf(" ]"); break; + case 0xf7: sciprintf(" #"); break; + case 0xf8: sciprintf(" <"); break; + case 0xf9: sciprintf(" >"); break; + case 0xff: break; + } + } while (nextitem != 0xff); + + sciprintf("\n"); +} + + +#ifdef SCI_SIMPLE_SAID_CODE + +static short _related_words[][2] = { /* 0 is backwards, 1 is forward */ + {0x800, 0x180}, /* preposition */ + {0x000, 0x180}, /* article */ + {0x000, 0x180}, /* adjective */ + {0x800, 0x000}, /* pronoun */ + {0x800, 0x180}, /* noun */ + {0x000, 0x800}, /* auxiliary verb */ + {0x800, 0x800}, /* adverb */ + {0x000, 0x180}, /* verb */ + {0x000, 0x180} /* number */ +}; + +int +vocab_build_simple_parse_tree(parse_tree_node_t *nodes, result_word_t *words, int words_nr) +{ + int i, length, pos = 0; + + for (i = 0; i < words_nr; ++i) { + if (words[i].class != VOCAB_CLASS_ANYWORD) { + nodes[pos].type = words[i].class; + nodes[pos].content.value = words[i].group; + pos += 2; /* Link information is filled in below */ + } + } + nodes[pos].type = -1; /* terminate */ + length = pos >> 1; + + /* now find all referenced words */ +#ifdef SCI_SIMPLE_SAID_DEBUG + sciprintf("Semantic references:\n"); +#endif + + for (i = 0; i < length; i++) { + int j; + int searchmask; + int type; + + pos = (i << 1); + type = sci_ffs(nodes[pos].type); + + if (type) { + int found = -1; + + type -= 5; /* 1 because ffs starts counting at 1, 4 because nodes[pos].type is a nibble off */ + if (type < 0) + type = 0; +#ifdef SCI_SIMPLE_SAID_DEBUG + sciprintf("#%d: Word %04x: type %04x\n", i, nodes[pos].content.value, type); +#endif + + /* search backwards */ + searchmask = _related_words[type][0]; + if (searchmask) { + for (j = i-1; j >= 0; j--) + if (nodes[j << 1].type & searchmask) { + found = j << 1; + break; + } + } + nodes[pos+1].content.branches[0] = found; +#ifdef SCI_SIMPLE_SAID_DEBUG + if (found > -1) + sciprintf(" %d <\n", found >> 1); +#endif + + /* search forward */ + found = -1; + searchmask = _related_words[type][1]; + if (searchmask) { + for (j = i+1; j < length; j++) + if (nodes[j << 1].type & searchmask) { + found = j << 1; + break; + } + } +#ifdef SCI_SIMPLE_SAID_DEBUG + if (found > -1) + sciprintf(" > %d\n", found >> 1); +#endif + + } else { +#ifdef SCI_SIMPLE_SAID_DEBUG + sciprintf("#%d: Untypified word\n", i); /* Weird, but not fatal */ +#endif + nodes[pos+1].content.branches[0] = -1; + nodes[pos+1].content.branches[1] = -1; + } + } +#ifdef SCI_SIMPLE_SAID_DEBUG + sciprintf("/Semantic references.\n"); +#endif + + return 0; +} +#endif + +result_word_t * +vocab_tokenize_string(char *sentence, int *result_nr, + word_t **words, int words_nr, + suffix_t **suffices, int suffices_nr, + char **error) +{ + char *lastword = sentence; + int pos_in_sentence = 0; + char c; + int wordlen = 0; + result_word_t *retval = (result_word_t*)sci_malloc(sizeof(result_word_t)); + /* malloc'd size is always one result_word_t too big */ + + result_word_t *lookup_result; + + + *result_nr = 0; + *error = NULL; + + do { + + c = sentence[pos_in_sentence++]; + + if (isalnum(c) || (c=='-' && wordlen)) + ++wordlen; /* Continue on this word */ + /* Words may contain a '-', but may not + ** start with one. */ + + else { + + if (wordlen) { /* Finished a word? */ + + lookup_result = + vocab_lookup_word(lastword, wordlen, + words, words_nr, + suffices, suffices_nr); + /* Look it up */ + + if (!lookup_result) { /* Not found? */ + *error = (char*)sci_calloc(wordlen + 1, 1); + strncpy(*error, lastword, wordlen); /* Set the offending word */ + free(retval); + return NULL; /* And return with error */ + } + + memcpy(retval + *result_nr, lookup_result, sizeof(result_word_t)); + /* Copy into list */ + ++(*result_nr); /* Increase number of resulting words */ + free(lookup_result); + + retval = (result_word_t*)sci_realloc(retval, sizeof(result_word_t) * (*result_nr + 1)); + + } + + lastword = sentence + pos_in_sentence; + wordlen = 0; + } + + } while (c); /* Until terminator is hit */ + + if (*result_nr == 0) { + free(retval); + return NULL; + } + + return retval; +} + + +void +_vocab_recursive_ptree_dump_treelike(parse_tree_node_t *nodes, int nr, int prevnr) +{ + if ((nr > VOCAB_TREE_NODES)/* || (nr < prevnr)*/) { + sciprintf("Error(%04x)", nr); + return; + } + + if (nodes[nr].type == PARSE_TREE_NODE_LEAF) + /* sciprintf("[%03x]%04x", nr, nodes[nr].content.value); */ + sciprintf("%x", nodes[nr].content.value); + else { + int lbranch = nodes[nr].content.branches[0]; + int rbranch = nodes[nr].content.branches[1]; + /* sciprintf("<[%03x]",nr); */ + sciprintf("<"); + + if (lbranch) + _vocab_recursive_ptree_dump_treelike(nodes, lbranch, nr); + else sciprintf("NULL"); + + sciprintf(","); + + if (rbranch) + _vocab_recursive_ptree_dump_treelike(nodes, rbranch, nr); + else sciprintf("NULL"); + + sciprintf(">"); + } +} + +void +_vocab_recursive_ptree_dump(parse_tree_node_t *nodes, int nr, int prevnr, int blanks) +{ + int lbranch = nodes[nr].content.branches[0]; + int rbranch = nodes[nr].content.branches[1]; + int i; + + if (nodes[nr].type == PARSE_TREE_NODE_LEAF) { + sciprintf("vocab_dump_parse_tree: Error: consp is nil for element %03x\n", nr); + return; + } + + if ((nr > VOCAB_TREE_NODES)/* || (nr < prevnr)*/) { + sciprintf("Error(%04x))", nr); + return; + } + + if (lbranch) { + if (nodes[lbranch].type == PARSE_TREE_NODE_BRANCH) { + sciprintf("\n"); + for (i = 0; i < blanks; i++) + sciprintf(" "); + sciprintf("("); + _vocab_recursive_ptree_dump(nodes, lbranch, nr, blanks + 1); + sciprintf(")\n"); + for (i = 0; i < blanks; i++) + sciprintf(" "); + } else + sciprintf("%x", nodes[lbranch].content.value); + sciprintf(" "); + }/* else sciprintf ("nil"); */ + + if (rbranch) { + if (nodes[rbranch].type == PARSE_TREE_NODE_BRANCH) + _vocab_recursive_ptree_dump(nodes, rbranch, nr, blanks); + else + sciprintf("%x", nodes[rbranch].content.value); + }/* else sciprintf("nil");*/ +} + +void +vocab_dump_parse_tree(const char *tree_name, parse_tree_node_t *nodes) +{ + /* _vocab_recursive_ptree_dump_treelike(nodes, 0, 0); */ + sciprintf("(setq %s \n'(", tree_name); + _vocab_recursive_ptree_dump(nodes, 0, 0, 1); + sciprintf("))\n"); +} + +void +vocab_synonymize_tokens(result_word_t *words, int words_nr, synonym_t *synonyms, int synonyms_nr) +{ + int i, sync; + + if (!synonyms || !synonyms_nr) + return; /* No synonyms: Nothing to check */ + + for (i = 0; i < words_nr; i++) + for(sync = 0; sync < synonyms_nr; sync++) + if (words[i].group == synonyms[sync].replaceant) + words[i].group = synonyms[sync].replacement; +} diff --git a/engines/sci/scicore/vocab_debug.c b/engines/sci/scicore/vocab_debug.c new file mode 100644 index 0000000000..1877037040 --- /dev/null +++ b/engines/sci/scicore/vocab_debug.c @@ -0,0 +1,421 @@ +/* Modified 07/18/99 by Christoph Reichenbach: +** - added vocabulary_free_snames(); +*/ + + +#include +#include +#include + +/* Default kernel name table */ +#define SCI0_KNAMES_WELL_DEFINED 0x6e +#define SCI0_KNAMES_DEFAULT_ENTRIES_NR 0x72 + +const char *sci0_default_knames[SCI0_KNAMES_DEFAULT_ENTRIES_NR] = +{ +/*0x00*/ "Load", +/*0x01*/ "UnLoad", +/*0x02*/ "ScriptID", +/*0x03*/ "DisposeScript", +/*0x04*/ "Clone", +/*0x05*/ "DisposeClone", +/*0x06*/ "IsObject", +/*0x07*/ "RespondsTo", +/*0x08*/ "DrawPic", +/*0x09*/ "Show", +/*0x0a*/ "PicNotValid", +/*0x0b*/ "Animate", +/*0x0c*/ "SetNowSeen", +/*0x0d*/ "NumLoops", +/*0x0e*/ "NumCels", +/*0x0f*/ "CelWide", +/*0x10*/ "CelHigh", +/*0x11*/ "DrawCel", +/*0x12*/ "AddToPic", +/*0x13*/ "NewWindow", +/*0x14*/ "GetPort", +/*0x15*/ "SetPort", +/*0x16*/ "DisposeWindow", +/*0x17*/ "DrawControl", +/*0x18*/ "HiliteControl", +/*0x19*/ "EditControl", +/*0x1a*/ "TextSize", +/*0x1b*/ "Display", +/*0x1c*/ "GetEvent", +/*0x1d*/ "GlobalToLocal", +/*0x1e*/ "LocalToGlobal", +/*0x1f*/ "MapKeyToDir", +/*0x20*/ "DrawMenuBar", +/*0x21*/ "MenuSelect", +/*0x22*/ "AddMenu", +/*0x23*/ "DrawStatus", +/*0x24*/ "Parse", +/*0x25*/ "Said", +/*0x26*/ "SetSynonyms", +/*0x27*/ "HaveMouse", +/*0x28*/ "SetCursor", +/*0x29*/ "FOpen", +/*0x2a*/ "FPuts", +/*0x2b*/ "FGets", +/*0x2c*/ "FClose", +/*0x2d*/ "SaveGame", +/*0x2e*/ "RestoreGame", +/*0x2f*/ "RestartGame", +/*0x30*/ "GameIsRestarting", +/*0x31*/ "DoSound", +/*0x32*/ "NewList", +/*0x33*/ "DisposeList", +/*0x34*/ "NewNode", +/*0x35*/ "FirstNode", +/*0x36*/ "LastNode", +/*0x37*/ "EmptyList", +/*0x38*/ "NextNode", +/*0x39*/ "PrevNode", +/*0x3a*/ "NodeValue", +/*0x3b*/ "AddAfter", +/*0x3c*/ "AddToFront", +/*0x3d*/ "AddToEnd", +/*0x3e*/ "FindKey", +/*0x3f*/ "DeleteKey", +/*0x40*/ "Random", +/*0x41*/ "Abs", +/*0x42*/ "Sqrt", +/*0x43*/ "GetAngle", +/*0x44*/ "GetDistance", +/*0x45*/ "Wait", +/*0x46*/ "GetTime", +/*0x47*/ "StrEnd", +/*0x48*/ "StrCat", +/*0x49*/ "StrCmp", +/*0x4a*/ "StrLen", +/*0x4b*/ "StrCpy", +/*0x4c*/ "Format", +/*0x4d*/ "GetFarText", +/*0x4e*/ "ReadNumber", +/*0x4f*/ "BaseSetter", +/*0x50*/ "DirLoop", +/*0x51*/ "CanBeHere", +/*0x52*/ "OnControl", +/*0x53*/ "InitBresen", +/*0x54*/ "DoBresen", +/*0x55*/ "DoAvoider", +/*0x56*/ "SetJump", +/*0x57*/ "SetDebug", +/*0x58*/ "InspectObj", +/*0x59*/ "ShowSends", +/*0x5a*/ "ShowObjs", +/*0x5b*/ "ShowFree", +/*0x5c*/ "MemoryInfo", +/*0x5d*/ "StackUsage", +/*0x5e*/ "Profiler", +/*0x5f*/ "GetMenu", +/*0x60*/ "SetMenu", +/*0x61*/ "GetSaveFiles", +/*0x62*/ "GetCWD", +/*0x63*/ "CheckFreeSpace", +/*0x64*/ "ValidPath", +/*0x65*/ "CoordPri", +/*0x66*/ "StrAt", +/*0x67*/ "DeviceInfo", +/*0x68*/ "GetSaveDir", +/*0x69*/ "CheckSaveGame", +/*0x6a*/ "ShakeScreen", +/*0x6b*/ "FlushResources", +/*0x6c*/ "SinMult", +/*0x6d*/ "CosMult", +/*0x6e*/ "SinDiv", +/*0x6f*/ "CosDiv", +/*0x70*/ "Graph", +/*0x71*/ SCRIPT_UNKNOWN_FUNCTION_STRING +}; + + +int getInt(unsigned char* d) +{ + return d[0] | (d[1]<<8); +} + +int* vocabulary_get_classes(resource_mgr_t *resmgr, int* count) +{ + resource_t* r; + int *c; + unsigned int i; + + if((r = scir_find_resource(resmgr, sci_vocab, 996, 0)) == NULL) return 0; + + c= (int*)sci_malloc(sizeof(int)*r->size/2); + for(i=2; isize; i+=4) + { + c[i/4]=getInt(r->data+i); + } + *count=r->size/4; + + return c; +} + +int vocabulary_get_class_count(resource_mgr_t *resmgr) +{ + resource_t* r; + if((r = scir_find_resource(resmgr, sci_vocab, 996, 0))==0) return 0; + return r->size/4; +} + +char** vocabulary_get_snames(resource_mgr_t *resmgr, int* pcount, sci_version_t version) +{ + char** t; + int count; + int i,j; + int magic; + + resource_t* r = scir_find_resource(resmgr, sci_vocab, 997, 0); + + if (!r) /* No such resource? */ + return NULL; + + count=getInt(r->data) + 1; /* Counter is slightly off */ + + magic=((version==0) || (version>=SCI_VERSION_FTU_NEW_SCRIPT_HEADER))? 1 : 2; + + t= (char**)sci_malloc(sizeof(char*)*magic*(count+1)); + + j=0; + + for(i=0; idata+2+i*2); + int len=getInt(r->data+offset); + t[j]= (char*)sci_malloc(len+1); + memcpy(t[j], r->data+offset+2, len); + t[j][len]='\0'; + j++; + if ((version!=0) && (versiondata+offset+2, len); + t[j][len]='\0'; + j++; + } + } + + t[j]=0; + + if (pcount != NULL) *pcount=magic*count; + + return t; +} + +int +vocabulary_lookup_sname(char **snames_list, char *sname) +{ + int pos = 0; + while (snames_list[pos]) + { + if (!strcasecmp(sname, snames_list[pos])) return pos; + pos++; + } + + return -1; +} + +void +vocabulary_free_snames(char **snames_list) +{ + int pos = 0; + + while (snames_list[pos]) + free(snames_list[pos++]); + + free(snames_list); +} + +opcode* vocabulary_get_opcodes(resource_mgr_t *resmgr) +{ + opcode* o; + int count, i=0; + resource_t* r = scir_find_resource(resmgr, sci_vocab, VOCAB_RESOURCE_OPCODES, 0); + + /* if the resource couldn't be loaded, leave */ + if (r == NULL) { + fprintf(stderr,"unable to load vocab.%03d\n", VOCAB_RESOURCE_OPCODES); + return NULL; + } + + count=getInt(r->data); + + o= (opcode*)sci_malloc(sizeof(opcode)*256); + for(i=0; idata+2+i*2); + int len=getInt(r->data+offset)-2; + o[i].type=getInt(r->data+offset+2); + o[i].number=i; + o[i].name= (char*)sci_malloc(len+1); + memcpy(o[i].name, r->data+offset+4, len); + o[i].name[len]='\0'; +#ifdef VOCABULARY_DEBUG + printf("Opcode %02X: %s, %d\n", i, o[i].name, o[i].type); +#endif + } + for(i=count; i<256; i++) + { + o[i].type=0; + o[i].number=i; + o[i].name= (char*)sci_malloc(strlen("undefined")+1); + strcpy(o[i].name, "undefined"); + } + return o; +} + +void +vocabulary_free_opcodes(opcode *opcodes) +{ + int i; + if (!opcodes) + return; + + for (i = 0; i < 256; i++) { + if (opcodes[i].name) + free(opcodes[i].name); + } + free(opcodes); +} + + +/* Alternative kernel func names retriever. Required for KQ1/SCI (at least). */ +static char** _vocabulary_get_knames0alt(int *names, resource_t *r) +{ + unsigned int mallocsize = 32; + char **retval = (char**)sci_malloc(sizeof (char *) * mallocsize); + unsigned int i = 0, index = 0; + + while (index < r->size) { + + int slen = strlen((char *) r->data + index) + 1; + + retval[i] = (char*)sci_malloc(slen); + memcpy(retval[i++], r->data + index, slen); + /* Wouldn't normally read this, but the cleanup code wants to free() this */ + + index += slen; + + if (i == mallocsize) + retval = (char**)sci_realloc(retval, sizeof(char *) * (mallocsize <<= 1)); + + } + + *names = i + 1; + retval = (char**)sci_realloc(retval, sizeof(char *) * (i+2)); + retval[i] = (char*)sci_malloc(strlen(SCRIPT_UNKNOWN_FUNCTION_STRING) + 1); + strcpy(retval[i], SCRIPT_UNKNOWN_FUNCTION_STRING); + /* The mystery kernel function- one in each SCI0 package */ + + retval[i+1] = NULL; /* Required for cleanup */ + + return retval; +} + + +static char** vocabulary_get_knames0(resource_mgr_t *resmgr, int* names) +{ + char** t; + int count, i, index=2, empty_to_add = 1; + resource_t* r = scir_find_resource(resmgr, sci_vocab, VOCAB_RESOURCE_KNAMES, 0); + + + if (!r) { /* No kernel name table found? Fall back to default table */ + t = (char**)sci_malloc ((SCI0_KNAMES_DEFAULT_ENTRIES_NR + 1) * sizeof(char*)); + *names = SCI0_KNAMES_DEFAULT_ENTRIES_NR - 1; /* index of last element */ + + for (i = 0; i < SCI0_KNAMES_DEFAULT_ENTRIES_NR; i++) + t[i] = sci_strdup(sci0_default_knames[i]); + + t[SCI0_KNAMES_DEFAULT_ENTRIES_NR] = NULL; /* Terminate list */ + + return t; + } + + count=getInt(r->data); + + if (count > 1023) + return _vocabulary_get_knames0alt(names, r); + + if (count < SCI0_KNAMES_WELL_DEFINED) { + empty_to_add = SCI0_KNAMES_WELL_DEFINED - count; + sciprintf("Less than %d kernel functions; adding %d\n", SCI0_KNAMES_WELL_DEFINED, empty_to_add); + } + + t= (char**)sci_malloc(sizeof(char*)*(count+1 + empty_to_add)); + for(i=0; idata+index); + int len=getInt(r->data+offset); + /*fprintf(stderr,"Getting name %d of %d...\n", i, count);*/ + index+=2; + t[i]= (char*)sci_malloc(len+1); + memcpy(t[i], r->data + offset + 2, len); + t[i][len]='\0'; + } + + for (i = 0; i < empty_to_add; i++) { + t[count + i] = (char*)sci_malloc(strlen(SCRIPT_UNKNOWN_FUNCTION_STRING) +1); + strcpy(t[count + i], SCRIPT_UNKNOWN_FUNCTION_STRING); + } + + t[count+empty_to_add]=0; + *names=count + empty_to_add; + return t; +} + +/*NOTE: Untested*/ +static char** vocabulary_get_knames1(resource_mgr_t *resmgr, int *count) +{ + char** t=NULL; + unsigned int size=64, used=0, pos=0; + resource_t* r = scir_find_resource(resmgr, sci_vocab, VOCAB_RESOURCE_KNAMES, 0); + + while(possize) + { + int len; + if ((used==size-1)||(!t)) + { + size*=2; + t= (char**)sci_realloc(t, size*sizeof(char*)); + } + len=strlen((char *) r->data+pos); + t[used]= (char*)sci_malloc(len+1); + strcpy(t[used], (char *) r->data+pos); + used++; + pos+=len+1; + } + *count=used; + t= (char**)sci_realloc(t, (used+1)*sizeof(char*)); + t[used]=NULL; + return t; +} + +char** vocabulary_get_knames(resource_mgr_t *resmgr, int* count) +{ + switch(resmgr->sci_version) + { + case SCI_VERSION_0: + case SCI_VERSION_01: + case SCI_VERSION_01_VGA: + case SCI_VERSION_01_VGA_ODD: return vocabulary_get_knames0(resmgr, count); + case SCI_VERSION_1_EARLY: + case SCI_VERSION_1_LATE: + case SCI_VERSION_1_1: + case SCI_VERSION_32: return vocabulary_get_knames1(resmgr, count); + default: return 0; + } +} + +void vocabulary_free_knames(char** names) +{ + int i = 0; + while(names[i]) + { + free(names[i]); + i++; + } + + free(names); +} diff --git a/engines/sci/sciv b/engines/sci/sciv new file mode 100644 index 0000000000..0a33725ce0 --- /dev/null +++ b/engines/sci/sciv @@ -0,0 +1,15 @@ +#!/bin/sh + +echo -------------------------[Depreciation warning]-------------------------------- +echo Using \'sciv\' to start FreeSCI is deprecated and will not be supported in future +echo versions of FreeSCI. Instead, use the \'freesci\' binary. +echo ------------------------------------------------------------------------------- +sleep 3 + +freesci $@ + +echo -------------------------[Depreciation warning]-------------------------------- +echo Using \'sciv\' to start FreeSCI is deprecated and will not be supported in future +echo versions of FreeSCI. Instead, use the \'freesci\' binary. +echo ------------------------------------------------------------------------------- + diff --git a/engines/sci/scummvm/detection.cpp b/engines/sci/scummvm/detection.cpp new file mode 100644 index 0000000000..c029657ed9 --- /dev/null +++ b/engines/sci/scummvm/detection.cpp @@ -0,0 +1,947 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2007 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with 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/advancedDetector.h" +#include "base/plugins.h" + +#include "scummvm_engine.h" + +// Titles of the games +static const PlainGameDescriptor SciGameTitles[] = { + {"sci", "Unknown SCI Game"}, + {"astrochicken", "Astro Chicken"}, + //{"christmas1988", "1988 Christmas Card"}, + //{"christmas1990", "1990 Christmas Card - The Seasoned Professional"}, + //{"christmas1992", "1992 Christmas Card"}, + {"castlebrain", "Castle of Dr. Brain"}, + {"iceman", "Codename: Iceman"}, + {"camelot", "Conquests of Camelot: King Arthur, Quest for the Grail"}, + {"longbow", "Conquests of the Longbow: The Adventures of Robin Hood"}, + {"ecoquest", "EcoQuest: The Search for Cetus"}, + //{"ecoquest2", "EcoQuest II: Lost Secret of the Rainforest"}, + {"freddypharkas", "Freddy Pharkas: Frontier Pharmacist"}, + {"gk1", "Gabriel Knight: Sins of the Fathers"}, + {"gk2", "The Beast Within: A Gabriel Knight Mystery"}, + {"hoyle1", "Hoyle's Official Book of Games: Volume 1"}, + {"hoyle2", "Hoyle's Official Book of Games: Volume 2"}, + {"hoyle3", "Hoyle's Official Book of Games: Volume 3"}, + {"jones", "Jones in the Fast Lane"}, + {"kq1sci", "King's Quest I: Quest for the Crown"}, + {"kq4", "King's Quest IV: The Perils of Rosella"}, + {"kq5", "King's Quest V: Absence Makes the Heart Go Yonder"}, + {"kq6", "King's Quest VI: Heir Today, Gone Tomorrow"}, + {"kq7", "King's Quest VII: The Princeless Bride"}, + {"laurabow", "Laura Bow: The Colonel's Bequest"}, + {"laurabow2", "Laura Bow 2: The Dagger of Amon Ra"}, + {"lsl1sci", "Leisure Suit Larry in the Land of the Lounge Lizards"}, + {"lsl2", "Leisure Suit Larry 2: Goes Looking for Love (in Several Wrong Places)"}, + {"lsl3", "Leisure Suit Larry 3: Passionate Patti in Pursuit of the Pulsating Pectorals"}, + {"lsl5", "Leisure Suit Larry 5: Passionate Patti Does a Little Undercover Work"}, + {"lsl6", "Leisure Suit Larry 6: Shape Up or Slip Out!"}, + {"lsl7", "Leisure Suit Larry 7: Love for Sail!"}, + {"lighthouse", "Lighthouse: The Dark Being"}, + //{"fairytales", "Mixed-up Fairy Tales"}, + {"mothergoose", "Mixed-Up Mother Goose"}, + //{"pepper", "Pepper's Adventure in Time"}, + {"phantasmagoria", "Phantasmagoria"}, + {"phantasmagoria2", "Phantasmagoria II: A Puzzle of Flesh"}, + {"pq1sci", "Police Quest: In Pursuit of the Death Angel"}, + {"pq2", "Police Quest II: The Vengeance"}, + {"pq3", "Police Quest III: The Kindred"}, + {"pq4", "Police Quest IV: Open Season"}, + {"qfg1", "Quest for Glory I: So You Want to Be a Hero"}, + {"qfg2", "Quest for Glory II: Trial by Fire"}, + {"qfg3", "Quest for Glory III: Wages of War"}, + {"qfg4", "Quest for Glory IV: Shadows of Darkness"}, + {"rama", "RAMA"}, + {"shivers", "Shivers"}, + //{"shivers2", "Shivers II: Harvest of Souls"}, + {"sq1sci", "Space Quest I: The Sarien Encounter"}, + {"sq3", "Space Quest III: The Pirates of Pestulon"}, + {"sq4", "Space Quest IV: Roger Wilco and the Time Rippers"}, + {"sq5", "Space Quest V: The Next Mutation"}, + {"sq6", "Space Quest 6: The Spinal Frontier"}, + {"islandbrain", "The Island of Dr. Brain"}, + {"torin", "Torin's Passage"}, + {0, 0} +}; + +// Game descriptions +static const struct SciGameDescription SciGameDescriptions[] = { + // Astro Chicken + {{"astrochicken", "", { + {"resource.map", 0, "f3d1be7752d30ba60614533d531e2e98", 474}, + {"resource.001", 0, "6fd05926c2199af0af6f72f90d0d7260", 126895}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Castle of Dr. Brain - English Amiga (from www.back2roots.org) + {{"castlebrain", "", { + {"resource.map", 0, "9f9fb826aa7e944b95eadbf568244a68", 2766}, + {"resource.000", 0, "0efa8409c43d42b32642f96652d3230d", 314773}, + {"resource.001", 0, "3fb02ce493f6eacdcc3713851024f80e", 559540}, + {"resource.002", 0, "d226d7d3b4f77c4a566913fc310487fc", 792380}, + {"resource.003", 0, "d226d7d3b4f77c4a566913fc310487fc", 464348}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Castle of Dr. Brain - German Amiga (from www.back2roots.org) + {{"castlebrain", "", { + {"resource.map", 0, "8e60424682db52a982bcc3535a7e86f3", 2796}, + {"resource.000", 0, "0efa8409c43d42b32642f96652d3230d", 332468}, + {"resource.001", 0, "4e0836fadc324316c1a418125709ba45", 569057}, + {"resource.002", 0, "85e51acb5f9c539d66e3c8fe40e17da5", 826309}, + {"resource.003", 0, "85e51acb5f9c539d66e3c8fe40e17da5", 493638}, + {NULL, 0, NULL, 0}}, Common::DE_DEU, Common::kPlatformAmiga, 0}, + {}}, + + // Castle of Dr. Brain - Spanish + {{"castlebrain", "", { + {"resource.map", 0, "5738c163e014bbe046474de009020b82", 2727}, + {"resource.000", 0, "27ec5fa09cd12a7fd16e86d96a2ed245", 1197694}, + {"resource.001", 0, "735be4e58957180cfc807d5e18fdffcd", 1433302}, + {NULL, 0, NULL, 0}}, Common::ES_ESP, Common::kPlatformPC, 0}, + {}}, + + // Codename: Iceman - English + {{"iceman", "", { + {"resource.map", 0, "a18f3cef4481a81d3415fb87a754343e", 5700}, + {"resource.000", 0, "b1bccd827453d4cb834bfd5b45bef63c", 26989}, + {"resource.001", 0, "32b351072fccf76fc82234d73d28c08b", 438872}, + {"resource.002", 0, "36670a917550757d57df84c96cf9e6d9", 566549}, + {"resource.003", 0, "d97a96f1ab91b41cf46a02cc89b0a04e", 624303}, + {"resource.004", 0, "8613c45fc771d658e5a505b9a4a54f31", 670883}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Codename: Iceman - English Amiga (from www.back2roots.org) + {{"iceman", "", { + {"resource.map", 0, "035829b391709a4e542d7c7b224625f6", 6000}, + {"resource.000", 0, "b1bccd827453d4cb834bfd5b45bef63c", 73682}, + {"resource.001", 0, "ede5a0e1e2a80fb629dae72c72f33d37", 293145}, + {"resource.002", 0, "36670a917550757d57df84c96cf9e6d9", 469387}, + {"resource.003", 0, "d97a96f1ab91b41cf46a02cc89b0a04e", 619219}, + {"resource.004", 0, "8613c45fc771d658e5a505b9a4a54f31", 713382}, + {"resource.005", 0, "605b67a9ef199a9bb015745e7c004cf4", 478384}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Conquests of Camelot - English + {{"camelot", "", { + {"resource.map", 0, "95eca3991906dfd7ed26d193df07596f", 7278}, + {"resource.001", 0, "8e1a3a8c588007404b532b8dfacc1460", 596774}, + {"resource.002", 0, "8e1a3a8c588007404b532b8dfacc1460", 722250}, + {"resource.003", 0, "8e1a3a8c588007404b532b8dfacc1460", 723712}, + {"resource.004", 0, "8e1a3a8c588007404b532b8dfacc1460", 729143}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Conquests of Camelot - English Amiga (from www.back2roots.org) + {{"camelot", "", { + {"resource.map", 0, "51aba42f8e63b219755d4372ea424509", 6654}, + {"resource.000", 0, "dfadf0b4c9fb44ce55570149856c302d", 128100}, + {"resource.001", 0, "67391de361b9347f123ac0899b4b91f7", 300376}, + {"resource.002", 0, "8c7f12b2c38d225d4c7121b30ea1b4d2", 605334}, + {"resource.003", 0, "82a73e7572e7ee627429bb5111ff82ca", 672392}, + {"resource.004", 0, "6821dc97cf643ba521a4e840dda3c58b", 647410}, + {"resource.005", 0, "c6e551bdc24f0acc193159038d4ca767", 605882}, + {"resource.006", 0, "8f880a536908ab496bbc552f7f5c3738", 585255}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Conquests of the Longbow - English + {{"longbow", "", { + {"resource.map", 0, "247f955865572569342751de47e861ab", 6027}, + {"resource.000", 0, "36e8fda5d0b8c49e587c8a9617959f72", 1297120}, + {"resource.001", 0, "1e6084a19f7a6c50af88d3a9b32c411e", 1366155}, + {"resource.002", 0, "7f6ce331219d58d5087731e4475ab4f1", 1234743}, + {"resource.003", 0, "1867136d01ece57b531032d466910522", 823686}, + {"resource.004", 0, "9cfce07e204a329e94fda8b5657621da", 1261462}, + {"resource.005", 0, "21ebe6b39b57a73fc449f67f013765aa", 1284720}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Conquests of the Longbow - English Amiga (from www.back2roots.org) + {{"longbow", "", { + {"resource.map", 0, "6204f3d00c0f6c0f5f95a29a4190f2f9", 6048}, + {"resource.000", 0, "8d11c744b4a51e7a8ceac687a46f08ca", 830333}, + {"resource.001", 0, "76caf8593e065a98c8ab4a6e2c7dbafc", 839008}, + {"resource.002", 0, "eb312373045906b54a3181bebaf6651a", 733145}, + {"resource.003", 0, "7fe3b3372d7fdda60045807e9c8e4867", 824554}, + {"resource.004", 0, "d1038c75d85a6650d48e07d174a6a913", 838175}, + {"resource.005", 0, "1c3804e56b114028c5873a35c2f06d13", 653002}, + {"resource.006", 0, "f9487732289a4f4966b4e34eea413325", 842817}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Eco Quest - Spanish Floppy + {{"ecoquest", "Floppy", { + {"resource.map", 0, "82e6b1e3bdb2f064b18380009df7b345", 4395}, + {"resource.000", 0, "0b12a91c935e385308af8d17811deded", 1004085}, + {"resource.001", 0, "2fed7451bca81b0c891eed1a956f2263", 1212060}, + {"resource.002", 0, "2d21a1d2dcbffa551552e3e0725d2284", 1186033}, + {"resource.003", 0, "84dd11b6825255671c703aee5ceff620", 1174993}, + {NULL, 0, NULL, 0}}, Common::ES_ESP, Common::kPlatformPC, 0}, + {}}, + + // Freddy Pharkas - Spanish CD + {{"freddypharkas", "CD", { + {"resource.map", 0, "a32674e7fbf7b213b4a066c8037f16b6", 5816}, + {"resource.000", 0, "fed4808fdb72486908ac7ad0044b14d8", 1456640}, + {"resource.001", 0, "15298fac241b5360763bfb68add1db07", 1456640}, + {"resource.002", 0, "419dbd5366f702b4123dedbbb0cffaae", 1456640}, + {"resource.003", 0, "05acdc256c742e79c50b9fe7ec2cc898", 863310}, + {NULL, 0, NULL, 0}}, Common::ES_ESP, Common::kPlatformPC, 0}, + {}}, + + // Gabriel Knight - English CD + {{"gk1", "CD", { + {"resource.map", 0, "372d059f75856afa6d73dd84cbb8913d", 10996}, + {"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 12581736}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Gabriel Knight - Spanish CD + {{"gk1", "CD", { + {"resource.map", 0, "7cb6e9bba15b544ec7a635c45bde9953", 11404}, + {"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13381599}, + {NULL, 0, NULL, 0}}, Common::ES_ESP, Common::kPlatformPC, 0}, + {}}, + + // Gabriel Knight 2 - English + {{"gk2", "", { + {"resmap.001", 0, "1b8bf6a23b37ed67358eb825fc687260", 2776}, + {"ressci.001", 0, "24463ae235b1afbbc4ff5e2ed1b8e3b2", 50496082}, + {"resmap.002", 0, "2028230674bb54cd24370e0745e7a9f4", 1975}, + {"ressci.002", 0, "f0edc1dcd704bd99e598c5a742dc7150", 42015676}, + {"resmap.003", 0, "51f3372a2133c406719dafad86369be3", 1687}, + {"ressci.003", 0, "86cb3f3d176994e7f8a9ad663a4b907e", 35313750}, + {"resmap.004", 0, "0f6e48f3e84e867f7d4a5215fcff8d5c", 2719}, + {"ressci.004", 0, "4f30aa6e6f895132402c8652f9e1d741", 58317316}, + {"resmap.005", 0, "2dac0e232262b4a51271fd28559b3e70", 2065}, + {"ressci.005", 0, "14b62d4a3bddee57a03cb1495a798a0f", 38075705}, + {"resmap.006", 0, "ce9359037277b7d7976da185c2fa0aad", 2977}, + {"ressci.006", 0, "8e44e03890205a7be12f45aaba9644b4", 60659424}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Hoyle 1 - English Amiga (from www.back2roots.org) + {{"hoyle1", "", { + {"resource.map", 0, "2a72b1aba65fa6e339370eb86d8601d1", 5166}, + {"resource.001", 0, "e0dd44069a62a463fd124974b915f10d", 218755}, + {"resource.002", 0, "e0dd44069a62a463fd124974b915f10d", 439502}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Hoyle 2 - English Amiga (from www.back2roots.org) + {{"hoyle2", "", { + {"resource.map", 0, "62ed48d20c580e5a98f102f7cd93706a", 1356}, + {"resource.001", 0, "8f2dd70abe01112eca464cda818b5eb6", 222704}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Hoyle 3 - English + {{"hoyle3", "", { + {"resource.map", 0, "7216a2972f9c595c45ab314941628e43", 2247}, + {"resource.000", 0, "6ef28cac094dcd97fdb461662ead6f92", 541845}, + {"resource.001", 0, "0a98a268ee99b92c233a0d7187c1f0fa", 845795}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Hoyle 3 - English Amiga (from www.back2roots.org) + {{"hoyle3", "", { + {"resource.map", 0, "f1f158e428398cb87fc41fb4aa8c2119", 2088}, + {"resource.000", 0, "595b6039ea1356e7f96a52c58eedcf22", 355791}, + {"resource.001", 0, "143df8aef214a2db34c2d48190742012", 632273}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Jones in the Fast Lane - English + {{"jones", "", { + {"resource.map", 0, "65cbe19b36fffc71c8e7b2686bd49ad7", 1800}, + {"resource.001", 0, "bac3ec6cb3e3920984ab0f32becf5163", 313476}, + {"resource.002", 0, "b86daa3ba2784d1502da881eedb80d9b", 719747}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // King's Quest 1 VGA Remake - English + {{"kq1sci", "VGA Remake", { + {"resource.map", 0, "7fe9399a0bec84ca5727309778d27f07", 5790}, + {"resource.001", 0, "fed9e0072ffd511d248674e60dee2099", 555439}, + {"resource.002", 0, "fed9e0072ffd511d248674e60dee2099", 714062}, + {"resource.003", 0, "fed9e0072ffd511d248674e60dee2099", 717478}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // King's Quest 1 VGA Remake - English Amiga (from www.back2roots.org) + {{"kq1sci", "VGA Remake", { + {"resource.map", 0, "37ed1a05eb719629eba15059c2eb6cbe", 6798}, + {"resource.001", 0, "9ae2a13708d691cd42f9129173c4b39d", 266621}, + {"resource.002", 0, "9ae2a13708d691cd42f9129173c4b39d", 795123}, + {"resource.003", 0, "9ae2a13708d691cd42f9129173c4b39d", 763224}, + {"resource.004", 0, "9ae2a13708d691cd42f9129173c4b39d", 820443}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // King's Quest 4 - English + {{"kq4", "", { + {"resource.map", 0, "3164a39790b599c954ecf716d0b32be8", 7476}, + {"resource.001", 0, "77615c595388acf3d1df8e107bfb6b52", 452523}, + {"resource.002", 0, "77615c595388acf3d1df8e107bfb6b52", 536573}, + {"resource.003", 0, "77615c595388acf3d1df8e107bfb6b52", 707591}, + {"resource.004", 0, "77615c595388acf3d1df8e107bfb6b52", 479562}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // King's Quest 4 - English Amiga (from www.back2roots.org) + {{"kq4", "", { + {"resource.map", 0, "f88dd267fb9504d40a04d599c048d42b", 6354}, + {"resource.000", 0, "77615c595388acf3d1df8e107bfb6b52", 138523}, + {"resource.001", 0, "52c2231765eced34faa7f7bcff69df83", 44751}, + {"resource.002", 0, "fb351106ec865fad9af5d78bd6b8e3cb", 663629}, + {"resource.003", 0, "fd16c9c223f7dc5b65f06447615224ff", 683016}, + {"resource.004", 0, "3fac034c7d130e055d05bc43a1f8d5f8", 549993}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // King's Quest 5 - English + {{"kq5", "", { + {"resource.map", 0, "f68ba690e5920725dcf9328001b90e33", 13122}, + {"resource.000", 0, "449471bfd77be52f18a3773c7f7d843d", 571368}, + {"resource.001", 0, "b45a581ff8751e052c7e364f58d3617f", 16800210}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // King's Quest 5 - English Amiga (from www.back2roots.org) + {{"kq5", "", { + {"resource.map", 0, "fcbcca058e1157221ffc27251cd59bc3", 8040}, + {"resource.000", 0, "c595ca99e7fa9b2cabcf69cfab0caf67", 344909}, + {"resource.001", 0, "964a3be90d810a99baf72ea70c09f935", 836477}, + {"resource.002", 0, "d10f3e8ff2cd95a798b21cd08797b694", 814730}, + {"resource.003", 0, "f72fdd994d9ba03a8360d639f256344e", 804882}, + {"resource.004", 0, "a5b80f95c66b3a032348989408eec287", 747914}, + {"resource.005", 0, "31a5487f4d942e6354d5be49d59707c9", 834146}, + {"resource.006", 0, "26c0c25399b6715fec03fc3e12544fe3", 823048}, + {"resource.007", 0, "b914b5901e786327213e779725d30dd1", 778772}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // King's Quest 6 - English + {{"kq6", "", { + {"resource.map", 0, "7a550ebfeae2575ca00d47703a6a774c", 9215}, + {"resource.000", 0, "233394a5f33b475ae5975e7e9a420865", 8376352}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // King's Quest 7 - English + {{"kq7", "", { + {"resource.map", 0, "2be9ab94429c721af8e05c507e048a15", 18697}, + {"resource.000", 0, "eb63ea3a2c2469dc2d777d351c626404", 203882535}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // King's Quest 7 - Spanish + {{"kq7", "", { + {"resource.map", 0, "0b62693cbe87e3aaca3e8655a437f27f", 18709}, + {"resource.000", 0, "51c1ead1163e19a2de8f121c39df7a76", 200764100}, + {NULL, 0, NULL, 0}}, Common::ES_ESP, Common::kPlatformPC, 0}, + {}}, + + // Laura Bow - English Amiga (from www.back2roots.org) + {{"laurabow", "", { + {"resource.map", 0, "731ab85e138f8cef1a7f4d1f36dfd375", 7422}, + {"resource.000", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 126317}, + {"resource.001", 0, "42fe895e9eb60e103025fd9ca737a849", 264763}, + {"resource.002", 0, "6f1ebd3692ce76644e0e06a38b7b56b5", 677436}, + {"resource.003", 0, "2ab23f64306b18c28302c8ec2964c5d6", 605134}, + {"resource.004", 0, "aa553977f7e5804081de293800d3bcce", 695067}, + {"resource.005", 0, "bfd870d51dc97729f0914095f58e6957", 676881}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Laura Bow 2 - Spanish CD + {{"laurabow2", "CD", { + {"resource.map", 0, "3b6dfbcda210bbc3f23fd1927113bf98", 6483}, + {"resource.000", 0, "57084910bc923bff5d6d9bc1b56e9604", 5028766}, + {NULL, 0, NULL, 0}}, Common::ES_ESP, Common::kPlatformPC, 0}, + {}}, + + // Larry 1 EGA Remake - English (from spookypeanut) + {{"lsl1sci", "EGA Remake", { + {"resource.map", 0, "abc0dc50c55de5b9723bb6de193f8756", 3282}, + {"resource.000", 0, "d3bceaebef3f7be941c2038b3565161e", 451366}, + {"resource.001", 0, "38936d3c68b6f79d3ffb13955713fed7", 591352}, + {"resource.002", 0, "24c958bc922b07f91e25e8c93aa01fcf", 491230}, + {"resource.003", 0, "685cd6c1e05a695ab1e0db826337ee2a", 553279}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Larry 1 VGA Remake - English (from spookypeanut) + {{"lsl1sci", "VGA Remake", { + {"resource.map", 0, "6d04d26466337a1a64b8c6c0eb65c9a9", 3222}, + {"resource.000", 0, "d3bceaebef3f7be941c2038b3565161e", 922406}, + {"resource.001", 0, "ec20246209d7b19f38989261e5c8f5b8", 1111226}, + {"resource.002", 0, "85d6935ef77e6b0e16bc307640a0d913", 1088312}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Larry 1 VGA Remake - English Amiga (from www.back2roots.org) + {{"lsl1sci", "VGA Remake", { + {"resource.map", 0, "7d115a9e27dc8ac71e8d5ef33d589bd5", 3366}, + {"resource.000", 0, "e67fd129d5810fc7ad8ea509d891cc00", 363073}, + {"resource.001", 0, "24ed6dc01b1e7fbc66c3d63a5994549a", 750465}, + {"resource.002", 0, "5790ac0505f7ca98d4567132b875eb1e", 681041}, + {"resource.003", 0, "4a34c3367c2fe7eb380d741374da1989", 572251}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Larry 1 VGA Remake - Spanish + {{"lsl1sci", "VGA Remake", { + {"resource.map", 0, "4fbe5c25878d51d7b2a68b710de4491b", 3327}, + {"resource.000", 0, "5e501a9bf8c753bf4c96158042422f00", 839172}, + {"resource.001", 0, "112648995dbc194037f1e4ed2e195910", 1063341}, + {"resource.002", 0, "3fe2a3aec0ed53c7d6db1845a67e3aa2", 1095908}, + {"resource.003", 0, "ac175df0ea9a2cba57f0248651856d27", 376556}, + {NULL, 0, NULL, 0}}, Common::ES_ESP, Common::kPlatformPC, 0}, + {}}, + + // Larry 2 - English + {{"lsl2", "", { + {"resource.map", 0, "42258cf767a8ebaa9e66b6151a80e601", 5628}, + {"resource.001", 0, "4a24443a25e2b1492462a52809605dc2", 143847}, + {"resource.002", 0, "4a24443a25e2b1492462a52809605dc2", 348331}, + {"resource.003", 0, "4a24443a25e2b1492462a52809605dc2", 236550}, + {"resource.004", 0, "4a24443a25e2b1492462a52809605dc2", 204861}, + {"resource.005", 0, "4a24443a25e2b1492462a52809605dc2", 277732}, + {"resource.006", 0, "4a24443a25e2b1492462a52809605dc2", 345683}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Larry 2 - English Amiga (from www.back2roots.org) + {{"lsl2", "", { + {"resource.map", 0, "e36ce0fc94d1678d15acbf12d84ec47d", 6612}, + {"resource.001", 0, "a0d4a625311d307257da7fc43d00459d", 409124}, + {"resource.002", 0, "a0d4a625311d307257da7fc43d00459d", 630106}, + {"resource.003", 0, "a0d4a625311d307257da7fc43d00459d", 570356}, + {"resource.004", 0, "a0d4a625311d307257da7fc43d00459d", 717844}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Larry 3 - English + {{"lsl3", "", { + {"resource.map", 0, "0b6bd3e039682830a51c5755c06591db", 5916}, + {"resource.001", 0, "f18441027154292836b973c655fa3175", 456722}, + {"resource.002", 0, "f18441027154292836b973c655fa3175", 578024}, + {"resource.003", 0, "f18441027154292836b973c655fa3175", 506807}, + {"resource.004", 0, "f18441027154292836b973c655fa3175", 513651}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Larry 3 - English Amiga (from www.back2roots.org) + {{"lsl3", "", { + {"resource.map", 0, "4a6da6322ce189431b5ffbac992bad3a", 5328}, + {"resource.000", 0, "cdc2e21e297b10fe8fed6377af8c5698", 66523}, + {"resource.001", 0, "6abbaf8c7e3b36dd868868ed187e8995", 71761}, + {"resource.002", 0, "a883424fe6d594fec0cd5a79e7ad54c8", 476490}, + {"resource.003", 0, "5c10e462c8cf589610773e4fe8bfd996", 527238}, + {"resource.004", 0, "f408e59cbee1457f042e5773b8c53951", 651634}, + {"resource.005", 0, "433911eb764089d493aed1f958a5615a", 524259}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Larry 5 - English (from spookypeanut) + {{"lsl5", "", { + {"resource.map", 0, "be00ef895197754ae4eab021ca44cbcd", 6417}, + {"resource.000", 0, "f671ab479df0c661b19cd16237692846", 726823}, + {"resource.001", 0, "db4a1381d88028876a99303bfaaba893", 751296}, + {"resource.002", 0, "d39d8db1a1e7806e7ccbfea3ef22df44", 1137646}, + {"resource.003", 0, "13fd4942bb818f9acd2970d66fca6509", 768599}, + {"resource.004", 0, "999f407c9f38f937d4b8c4230ff5bb38", 1024516}, + {"resource.005", 0, "0cc8d35a744031c772ca7cd21ae95273", 1011944}, + {"resource.006", 0, "dda27ce00682aa76198dac124bbbe334", 1024810}, + {"resource.007", 0, "ac443fae1285fb359bf2b2bc6a7301ae", 1030656}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Larry 5 - English Amiga (from www.back2roots.org) + {{"lsl5", "", { + {"resource.map", 0, "e36052ae0c8b14d6f074bcb0aee50a38", 6096}, + {"resource.000", 0, "d8b58ce10de52aa16f8b2006838c4fcc", 310510}, + {"resource.001", 0, "8caa8fbb50ea43f3efdfb66f1e68998b", 800646}, + {"resource.002", 0, "abdaa299e00c908052d33cd82eb60e9b", 784576}, + {"resource.003", 0, "810ad1d61638c27a780576cb09f18ed7", 805941}, + {"resource.004", 0, "3ce5901f1bc171ac0274d99a4eeb9e57", 623022}, + {"resource.005", 0, "f8b2d1137bb767e5d232056b99dd69eb", 623621}, + {"resource.006", 0, "bafc64e3144f115dc58c6aee02de98fb", 715598}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Larry 5 - Spanish + {{"lsl5", "", { + {"resource.map", 0, "b6f7da7bf24e5a6b2946032cec3ea59c", 6861}, + {"resource.000", 0, "4c00c14b8181ad47076a51d86097d97e", 765418}, + {"resource.001", 0, "245c44f8ccd796732e61857e67b30079", 916028}, + {"resource.002", 0, "e86aeb27711f4a673e06ec32cfc84125", 929645}, + {"resource.003", 0, "74edc89d8c1cb346ca346081b927e4c6", 1005496}, + {"resource.004", 0, "999f407c9f38f937d4b8c4230ff5bb38", 1021996}, + {"resource.005", 0, "0cc8d35a744031c772ca7cd21ae95273", 958079}, + {"resource.006", 0, "dda27ce00682aa76198dac124bbbe334", 1015136}, + {"resource.007", 0, "ac443fae1285fb359bf2b2bc6a7301ae", 987222}, + {NULL, 0, NULL, 0}}, Common::ES_ESP, Common::kPlatformPC, 0}, + {}}, + + // Larry 6 - English (from spookypeanut) + {{"lsl6", "", { + {"resource.map", 0, "bb8a39d9e2a77ba449a1e591109ad9a8", 6973}, + {"resource.000", 0, "4462fe48c7452d98fddcec327a3e738d", 5789138}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Larry 6 - Spanish + {{"lsl6", "", { + {"resource.map", 0, "633bf8f42170b6271019917c8009989b", 6943}, + {"resource.000", 0, "7884a8db9253e29e6b37a2651fd90ba3", 5733116}, + {NULL, 0, NULL, 0}}, Common::ES_ESP, Common::kPlatformPC, 0}, + {}}, + + // Larry 7 - English (from spookypeanut) + {{"lsl7", "", { + {"resmap.000", 0, "eae93e1b1d1ccc58b4691c371281c95d", 8188}, + {"ressci.000", 0, "89353723488219e25589165d73ed663e", 66965678}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Larry 7 - Spanish + {{"lsl7", "", { + {"resmap.000", 0, "8f3d603e1acc834a5d598b30cdfc93f3", 8188}, + {"ressci.000", 0, "32792f9bc1bf3633a88b382bb3f6e40d", 67071418}, + {NULL, 0, NULL, 0}}, Common::ES_ESP, Common::kPlatformPC, 0}, + {}}, + + // Lighthouse - English + {{"lighthouse", "", { + {"resmap.001", 0, "47abc502c0b541b582db28f38dbc6a56", 7801}, + {"ressci.001", 0, "14e922c47b92156377cb49e241691792", 99591924}, + {"resmap.002", 0, "c68db5333f152fea6ca2dfc75cad8b34", 7573}, + {"ressci.002", 0, "175468431a979b9f317c294ce3bc1430", 94628315}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Lighthouse - Spanish + {{"lighthouse", "", { + {"resmap.001", 0, "c5d49b2a8a4eafc92fd041a3a0f2da68", 7846}, + {"ressci.001", 0, "18553177dbf83fb2cb6c8edcbb174183", 99543093}, + {"resmap.002", 0, "e7dc85884a2417e2eff9de0c63dd65fa", 7630}, + {"ressci.002", 0, "3c8d627c555b0e3e4f1d9955bc0f0df4", 94631127}, + {NULL, 0, NULL, 0}}, Common::ES_ESP, Common::kPlatformPC, 0}, + {}}, + + // Mixed-Up Mother Goose - English CD + {{"mothergoose", "CD", { + {"resource.map", 0, "1c7f311b0a2c927b2fbe81ae341fb2f6", 5790}, + {"resource.001", 0, "5a0ed1d745855148364de1b3be099bac", 4369438}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Mixed-Up Mother Goose - English Amiga (from www.back2roots.org) + {{"mothergoose", "", { + {"resource.map", 0, "4aa28ac93fae03cf854594da13d9229c", 2700}, + {"resource.001", 0, "fb552ae550ca1dac19ed8f6a3767612d", 262885}, + {"resource.002", 0, "fb552ae550ca1dac19ed8f6a3767612d", 817191}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Phantasmagoria - English + {{"phantasmagoria", "", { + {"resmap.001", 0, "416138651ea828219ca454cae18341a3", 11518}, + {"ressci.001", 0, "3aae6559aa1df273bc542d5ac6330d75", 65844612}, + {"resmap.002", 0, "de521d0c7ab32897e7fe58e421c816b7", 12058}, + {"ressci.002", 0, "3aae6559aa1df273bc542d5ac6330d75", 71588691}, + {"resmap.003", 0, "25df95bd7da3686f71a0af8784a2b8ca", 12334}, + {"ressci.003", 0, "3aae6559aa1df273bc542d5ac6330d75", 73651084}, + {"resmap.004", 0, "e108a3d35794f1721aeea3e62a3f8b3b", 12556}, + {"ressci.004", 0, "3aae6559aa1df273bc542d5ac6330d75", 75811935}, + {"resmap.005", 0, "390d81f9e14a3f3ee2ea477135f0288e", 12604}, + {"ressci.005", 0, "3aae6559aa1df273bc542d5ac6330d75", 78814934}, + {"resmap.006", 0, "8ea3c954606e80604680f9fe707f15d8", 12532}, + {"ressci.006", 0, "3aae6559aa1df273bc542d5ac6330d75", 77901360}, + {"resmap.007", 0, "afbd16ea77869a720afa1c5371de107d", 7972}, + //{"ressci.007", 0, "3aae6559aa1df273bc542d5ac6330d75", 25859038}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Phantasmagoria 2 - English + {{"phantasmagoria2", "", { + {"resmap.001", 0, "0a961e135f4f7effb195158325856633", 1108}, + {"ressci.001", 0, "53f457cddb0dffc056593905c4cbb989", 24379964}, + {"resmap.002", 0, "5d3189fe3d4f286f83c5c8031fa3e9f7", 1126}, + {"ressci.002", 0, "53f457cddb0dffc056593905c4cbb989", 34465805}, + {"resmap.003", 0, "c92e3c840b827c236ab6671c03760c56", 1162}, + {"ressci.003", 0, "53f457cddb0dffc056593905c4cbb989", 38606375}, + {"resmap.004", 0, "8d5cfe19365f71370b87063686f39171", 1288}, + {"ressci.004", 0, "53f457cddb0dffc056593905c4cbb989", 42447131}, + {"resmap.005", 0, "8bd5ceeedcbe16dfe55d1b90dcd4be84", 1942}, + {"ressci.005", 0, "05f9fe2bee749659acb3cd2c90252fc5", 67905112}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformWindows, 0}, + {}}, + + // Police Quest 1 VGA Remake - English + {{"pq1sci", "VGA Remake", { + {"resource.map", 0, "35efa814fb994b1cbdac9611e401da67", 5013}, + {"resource.000", 0, "e0d5ddf34eda903a38f0837e2aa7145b", 6401433}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Police Quest 2 - English + {{"pq2", "", { + {"resource.map", 0, "28a6f471c7900c2c92da40eecb615d9d", 4584}, + {"resource.001", 0, "77f02def3094af804fd2371db25b7100", 509525}, + {"resource.002", 0, "77f02def3094af804fd2371db25b7100", 546000}, + {"resource.003", 0, "77f02def3094af804fd2371db25b7100", 591851}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Police Quest 2 - English Amiga (from www.back2roots.org) + {{"pq2", "", { + {"resource.map", 0, "499de78ae72b7ce219f944c5e7ef0c5b", 3426}, + {"resource.000", 0, "77f02def3094af804fd2371db25b7100", 250232}, + {"resource.001", 0, "523db0c07f1da2a822c2c39ee0482544", 179334}, + {"resource.002", 0, "499737c21a28ac026e11ab817100d610", 511099}, + {"resource.003", 0, "e008f5d6e2a7c4d4a0da0173e4fa8f8b", 553970}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Police Quest 3 - English + {{"pq3", "", { + {"resource.map", 0, "6457bf0c8ca865a42d9ff5827ab49b89", 5559}, + {"resource.000", 0, "7659713720d61d9465a59091b7ee63ea", 737253}, + {"resource.001", 0, "61c7c187d25a8346be0a092d5f037278", 1196787}, + {"resource.002", 0, "c18e0d408e4f4f40365d42aa15931f67", 1153561}, + {"resource.003", 0, "8791b9eef53edf77c2dac950142221d3", 1159791}, + {"resource.004", 0, "1b91e891a3c60a941dac0eecdf83375b", 1143606}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Police Quest 3 - English Amiga (from www.back2roots.org) + {{"pq3", "", { + {"resource.map", 0, "29923fe1ef1f0909b57255d61c558e68", 5742}, + {"resource.000", 0, "4908e4f4977e8e19c90c29b36a741ffe", 298541}, + {"resource.001", 0, "0eb943ca807e2f69578821d490770d2c", 836567}, + {"resource.002", 0, "f7044bb08a1fcbe5077791ed8d4996f0", 691207}, + {"resource.003", 0, "630bfa65beb05f743552704ac2899dae", 759891}, + {"resource.004", 0, "7b229fbdf30d670d0728cede3e984a7e", 838663}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Police Quest 4 - English + {{"pq4", "", { + {"resource.map", 0, "379dfe80ed6bd16c47e4b950c4722eac", 11374}, + {"resource.000", 0, "fd316a09b628b7032248139003369022", 18841068}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Quest for Glory 1 - English + {{"qfg1", "", { + {"resource.map", 0, "74a108a7fb345bfc84f4113b6e5241bb", 6432}, + {"resource.000", 0, "40332d3ebfc70a4b6a6a0443c2763287", 79181}, + {"resource.001", 0, "917fcef303e9489597154727baaa9e07", 461422}, + {"resource.002", 0, "05ddce5f437a516b89ede2438fac09d8", 635734}, + {"resource.003", 0, "951299a82a8134ed12c5c18118d45c2f", 640483}, + {"resource.004", 0, "951299a82a8134ed12c5c18118d45c2f", 644443}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Quest for Glory 1 - English Amiga (from www.back2roots.org) + {{"qfg1", "", { + {"resource.map", 0, "e65034832f0c9df1dc22128227b782d0", 6066}, + {"resource.000", 0, "1c0255dea2d3cd71eee9f2db201eee3f", 111987}, + {"resource.001", 0, "a270012fa74445d74c044d1b65a9ff8c", 143570}, + {"resource.002", 0, "e64004e020fdf1813be52b639b08be89", 553201}, + {"resource.003", 0, "7ab2bf8e224b57f75e0cd6e4ba790761", 642203}, + {"resource.004", 0, "7ab2bf8e224b57f75e0cd6e4ba790761", 641688}, + {"resource.005", 0, "5f3386ef2f2b1254e4a066f5d9027324", 609529}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Quest for Glory 2 - English Amiga (from www.back2roots.org) + {{"qfg2", "", { + {"resource.map", 0, "365ea1033ba26d227ec4007be88c59cc", 7596}, + {"resource.000", 0, "810245be50fde5a67e3ea95e876e3e64", 233341}, + {"resource.001", 0, "7a5fde9875211ed67a896fc0d91940c8", 127294}, + {"resource.002", 0, "dcf6bc2c18660d7ad532fb61861eb721", 543644}, + {"resource.003", 0, "dcf6bc2c18660d7ad532fb61861eb721", 565044}, + {"resource.004", 0, "dcf6bc2c18660d7ad532fb61861eb721", 466630}, + {"resource.005", 0, "a77d2576c842b2b06da57d4ac8fc51c0", 579975}, + {"resource.006", 0, "ccf5dba33e5cab6d5872838c0f8db44c", 500039}, + {"resource.007", 0, "4c9fc1587545879295cb9627f56a2cb8", 575056}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Quest for Glory 3 - Spanish + {{"qfg3", "", { + {"resource.map", 0, "10809197c33a5e62819311d8a2f73f85", 5978}, + {"resource.000", 0, "ba7ac86155e4c531e46cd73c86daa80a", 5884098}, + {NULL, 0, NULL, 0}}, Common::ES_ESP, Common::kPlatformPC, 0}, + {}}, + + // Quest for Glory - English + {{"qfg4", "", { + {"resource.map", 0, "aba367f2102e81782d961b14fbe3d630", 10246}, + {"resource.000", 0, "263dce4aa34c49d3ad29bec889007b1c", 11571394}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // RAMA - English + {{"rama", "", { + {"resmap.001", 0, "3bac72a1910a563f8f92cf5b77c8b7f2", 8338}, + {"ressci.001", 0, "2a68edd064e5e4937b5e9c74b38f2082", 70588050}, + {"resmap.002", 0, "83c2aa4653a985ab4b49ff60532ed08f", 12082}, + {"ressci.002", 0, "2a68edd064e5e4937b5e9c74b38f2082", 128562138}, + {"resmap.003", 0, "31ef4c0621711585d031f0ae81707251", 1636}, + {"ressci.003", 0, "2a68edd064e5e4937b5e9c74b38f2082", 6860492}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformWindows, 0}, + {}}, + + // Shivers - English + {{"shivers", "", { + {"resmap.000", 0, "f2ead37749ed8f6535a2445a7d05a0cc", 46525}, + {"ressci.000", 0, "4294c6d7510935f2e0a52e302073c951", 262654836}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformWindows, 0}, + {}}, + + // Space Quest 1 VGA Remake - English + {{"sq1sci", "VGA Remake", { + {"resource.map", 0, "38a74d8f555a2da9ca4f21d14e3c1d33", 5913}, + {"resource.000", 0, "e9d866534f8c84de82e25f2631ff258c", 1016436}, + {"resource.001", 0, "a89b7b52064c75b1985b289edc2f5c69", 1038757}, + {"resource.002", 0, "a9e847c687529481f3a22b9bf01f45f7", 1169831}, + {"resource.003", 0, "c47600e50c6fc591957ae0c5020ee7b8", 1213262}, + {"resource.004", 0, "e19ea4ad131472f9238590f2e1d40289", 1203051}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Space Quest 1 VGA Remake - English Amiga (from www.back2roots.org) + {{"sq1sci", "VGA Remake", { + {"resource.map", 0, "106484b372af1d4cbf866472cc2813dc", 6396}, + {"resource.000", 0, "cc9d6ace343661ae51ec8bd6e6b00a8c", 340944}, + {"resource.001", 0, "59efcfa2268d2f8608f544e2674d8151", 761721}, + {"resource.002", 0, "f00ef883128bf5fc2fbb888cdd7adf25", 814461}, + {"resource.003", 0, "2588c1c2ca8b9bed0e3411948c0856a9", 839302}, + {"resource.004", 0, "b25a1539c71701f7715f738c5037e9a6", 775515}, + {"resource.005", 0, "640ffe1a9acde392cc33cc1b1a528328", 806324}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Space Quest 1 VGA Remake - Spanish + {{"sq1sci", "VGA Remake", { + {"resource.map", 0, "cee2a67fa7f8f1f520f398110ca1c37e", 6111}, + {"resource.000", 0, "945081a73211e0c40e62f709edcd8d1d", 970657}, + {"resource.001", 0, "94692dc84c85c93bb8850f58aebf3cfc", 1085687}, + {"resource.002", 0, "fc9ad3357e4cedec1611ad2b67b193a9", 1175465}, + {"resource.003", 0, "8c22700a02991b763f512f837636b3ca", 1211307}, + {"resource.004", 0, "9b78228ad4f9f335fedf74f1812dcfca", 513325}, + {"resource.005", 0, "7d4ebcb745c0bf8fc42e4013f52ecd49", 1101812}, + {NULL, 0, NULL, 0}}, Common::ES_ESP, Common::kPlatformPC, 0}, + {}}, + + // Space Quest 3 - English + {{"sq3", "", { + {"resource.map", 0, "55e91aeef1705bce2a9b79172682f36d", 5730}, + {"resource.001", 0, "8b55c4875298f45ea5696a5ee8f6a7fe", 490247}, + {"resource.002", 0, "8b55c4875298f45ea5696a5ee8f6a7fe", 715777}, + {"resource.003", 0, "8b55c4875298f45ea5696a5ee8f6a7fe", 703370}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Space Quest 3 - English Amiga (from www.back2roots.org) + {{"sq3", "", { + {"resource.map", 0, "bad41385acde6d677a8d55a7b20437e3", 5868}, + {"resource.001", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 171636}, + {"resource.002", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 754432}, + {"resource.003", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 746496}, + {"resource.004", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 761984}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0}, + {}}, + + // Space Quest 4 - English + {{"sq4", "", { + {"resource.map", 0, "ed90a8e3ccc53af6633ff6ab58392bae", 7054}, + {"resource.000", 0, "63247e3901ab8963d4eece73747832e0", 5157378}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Space Quest 4 - German Amiga (from www.back2roots.org) + {{"sq4", "", { + {"resource.map", 0, "79641c0d43408e33c251a1d494d2575e", 6252}, + {"resource.000", 0, "feff51c52146b3a31d4793c718279e13", 345170}, + {"resource.001", 0, "ab33060bfebe32450c0b8d9a3a066efc", 822470}, + {"resource.002", 0, "f79fd6a62da082addb205ed6cef99629", 810458}, + {"resource.003", 0, "f4c21da916f450d4b893b4cba6120866", 815854}, + {"resource.004", 0, "99c6a017da5e769a3b427ca52c8a564f", 824601}, + {"resource.005", 0, "10ee1709e6559c724676d058199b75b5", 818745}, + {"resource.006", 0, "67fb188b191d88efe8414af6ea297b93", 672675}, + {NULL, 0, NULL, 0}}, Common::DE_DEU, Common::kPlatformAmiga, 0}, + {}}, + + // Space Quest 4 - Spanish + {{"sq4", "", { + {"resource.map", 0, "51bcb305568ec19713f8b79727f10071", 6159}, + {"resource.000", 0, "8000a55aebc50a68b7cce07a8c33758c", 204315}, + {"resource.001", 0, "99a6df6d366b3f061271ff3450ac0d32", 1269094}, + {"resource.002", 0, "a6a8d7a24dbb7a266a26b084e7275e89", 1240998}, + {"resource.003", 0, "42a307941edeb1a3be31daeb2e4be90b", 1319306}, + {"resource.004", 0, "776fba81c110d1908776232cbe190e20", 1253752}, + {"resource.005", 0, "55fae26c2a92f16ef72c1e216e827c0f", 1098328}, + {NULL, 0, NULL, 0}}, Common::ES_ESP, Common::kPlatformPC, 0}, + {}}, + + // Space Quest 4 - Spanish + {{"sq4", "", { + {"resource.map", 0, "41543ae71036046fef69df29a838ee05", 5589}, + {"resource.000", 0, "2ac39ff61e369b79f3d7a4ad514f8e29", 242470}, + {"resource.001", 0, "567608beb69d9dffdb42a8f39cb11a5e", 994323}, + {"resource.002", 0, "74c62fa2146ff3b3b2ea2b3fb95b9af9", 1140801}, + {"resource.003", 0, "42a307941edeb1a3be31daeb2e4be90b", 1088408}, + {NULL, 0, NULL, 0}}, Common::ES_ESP, Common::kPlatformPC, 0}, + {}}, + + // Space Quest 5 - English + {{"sq5", "", { + {"resource.map", 0, "66317c12ac6e818d1f7c17e83c1d9819", 6143}, + {"resource.000", 0, "4147edc5045e6d62998018b5614c58ec", 5496486}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Space Quest 6 - English + {{"sq6", "", { + {"resource.map", 0, "6dddfa3a8f3a3a513ec9dfdfae955005", 10528}, + {"resource.000", 0, "c4259ab7355aead07773397b1052827d", 41150806}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // The Island of Dr. Brain - English + {{"islandbrain", "", { + {"resource.map", 0, "2388efef8430b041b0f3b00b9050e4a2", 3281}, + {"resource.000", 0, "b3acd9b9dd7fe53c4ee133ac9a1acfab", 2103560}, + {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0}, + {}}, + + // Torin's Passage - Spanish? (the cd print says spanish subtitles) + {{"torin", "", { + {"resmap.000", 0, "bb3b0b22ff08df54fbe2d06263409be6", 9799}, + {"ressci.000", 0, "693a259d346c9360f4a0c11fdaae430a", 55973887}, + {NULL, 0, NULL, 0}}, Common::ES_ESP, Common::kPlatformPC, 0}, + {}}, + + {AD_TABLE_END_MARKER, {}} +}; + +// Generic entries for filename based fallback +static const struct SciGameDescription SciGameGeneric[] = { + {{"sci", 0, AD_ENTRY1("resource.map", NULL), Common::UNK_LANG, Common::kPlatformUnknown, 0},{}}, + {AD_TABLE_END_MARKER, {}} +}; + +// Filename based fallback information +static const struct Common::ADFileBasedFallback SciGameFallback[] = { + {(const void*)&SciGameGeneric[0], {"resource.map", "resource.000", NULL} }, + {(const void*)&SciGameGeneric[0], {"resource.map", "resource.001", NULL} }, + {(const void*)&SciGameGeneric[0], {"resmap.000", "ressci.000", NULL} }, + {(const void*)&SciGameGeneric[0], {"resmap.001", "ressci.001", NULL} }, + {0, {NULL}} +}; + +static const Common::ADParams detectionParams = { + // Pointer to ADGameDescription or its superset structure + (const byte *)SciGameDescriptions, + // Size of that superset structure + sizeof(SciGameDescription), + // Number of bytes to compute MD5 sum for + 5000, + // List of all engine targets + SciGameTitles, + // Structure for autoupgrading obsolete targets + 0, + // Name of single gameid (optional) + "sci", + // List of files for file-based fallback detection (optional) + SciGameFallback, + // Flags + 0 +}; + +class SciMetaEngine : public Common::AdvancedMetaEngine { +public: + SciMetaEngine() : Common::AdvancedMetaEngine(detectionParams) {} + + virtual const char *getName() const { + return "SCI Engine"; + } + + virtual const char *getCopyright() const { + return "Sierra's Creative Interpreter (C) Sierra Online"; + } + + virtual bool createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *gd) const; +}; + + +bool SciMetaEngine::createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *gd) const { + const SciGameDescription *desc = (const SciGameDescription *)gd; + + *engine = new SciEngine(syst, desc); + + return true; + + /* + const char *gameid = ConfMan.get("gameid").c_str(); + + if (gd == 0) { + // maybe add non md5 based detection again? + return kNoGameDataFoundError; + } + + Kyra::GameFlags flags = gd->flags; + + flags.lang = gd->desc.language; + flags.platform = gd->desc.platform; + + Common::Platform platform = Common::parsePlatform(ConfMan.get("platform")); + if (platform != Common::kPlatformUnknown) { + flags.platform = platform; + } + + if (flags.lang == Common::UNK_LANG) { + Common::Language lang = Common::parseLanguage(ConfMan.get("language")); + if (lang != Common::UNK_LANG) { + flags.lang = lang; + } else { + flags.lang = Common::EN_ANY; + } + }*/ +} + +#if PLUGIN_ENABLED_DYNAMIC(SCI) + REGISTER_PLUGIN_DYNAMIC(SCI, PLUGIN_TYPE_ENGINE, SciMetaEngine); +#else + REGISTER_PLUGIN_STATIC(SCI, PLUGIN_TYPE_ENGINE, SciMetaEngine); +#endif diff --git a/engines/sci/scummvm/scummvm.patch b/engines/sci/scummvm/scummvm.patch new file mode 100644 index 0000000000..779f4fce7c --- /dev/null +++ b/engines/sci/scummvm/scummvm.patch @@ -0,0 +1,55 @@ +Index: configure +=================================================================== +--- configure (revision 32642) ++++ configure (working copy) +@@ -97,6 +97,7 @@ + add_engine parallaction "Parallaction" yes + add_engine queen "Flight of the Amazon Queen" yes + add_engine saga "SAGA" yes ++add_engine sci "SCI" no + add_engine sky "Beneath a Steel Sky" yes + add_engine sword1 "Broken Sword 1" yes + add_engine sword2 "Broken Sword 2" yes +Index: engines/engines.mk +=================================================================== +--- engines/engines.mk (revision 32642) ++++ engines/engines.mk (working copy) +@@ -82,6 +82,11 @@ + MODULES += engines/saga + endif + ++ifdef ENABLE_SCI ++DEFINES += -DENABLE_SCI=$(ENABLE_SCI) ++MODULES += engines/sci ++endif ++ + ifdef ENABLE_SKY + DEFINES += -DENABLE_SKY=$(ENABLE_SKY) + MODULES += engines/sky +Index: base/plugins.cpp +=================================================================== +--- base/plugins.cpp (revision 32642) ++++ base/plugins.cpp (working copy) +@@ -131,6 +131,9 @@ + #if PLUGIN_ENABLED_STATIC(SAGA) + LINK_PLUGIN(SAGA) + #endif ++ #if PLUGIN_ENABLED_STATIC(SCI) ++ LINK_PLUGIN(SCI) ++ #endif + #if PLUGIN_ENABLED_STATIC(SKY) + LINK_PLUGIN(SKY) + #endif +Index: Makefile +=================================================================== +--- Makefile (revision 32642) ++++ Makefile (working copy) +@@ -9,7 +9,7 @@ + + DEFINES := -DHAVE_CONFIG_H + LDFLAGS := +-INCLUDES := -I. -I$(srcdir) -I$(srcdir)/engines ++INCLUDES := -I. -I$(srcdir) #-I$(srcdir)/engines + LIBS := + OBJS := + DEPDIR := .deps diff --git a/engines/sci/scummvm/scummvm_engine.cpp b/engines/sci/scummvm/scummvm_engine.cpp new file mode 100644 index 0000000000..652666fc3e --- /dev/null +++ b/engines/sci/scummvm/scummvm_engine.cpp @@ -0,0 +1,416 @@ +/*************************************************************************** + main.c Copyright (C) 1999,2000,01,02 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + +#include "common/system.h" +#include "common/advancedDetector.h" +#include "common/config-manager.h" + +#include "scummvm_engine.h" +#include "engine.h" + +//namespace Sci { + +extern gfx_driver_t gfx_driver_scummvm; + +int +c_quit(state_t *s) +{ + script_abort_flag = 1; /* Terminate VM */ + _debugstate_valid = 0; + _debug_seeking = 0; + _debug_step_running = 0; + return 0; +} + +int +c_die(state_t *s) +{ + exit(0); /* Die */ + return 0; /* ;-P (fixes warning) */ +} + +static void +init_console() +{ +#ifdef WANT_CONSOLE + con_gfx_init(); +#endif + con_hook_command(&c_quit, "quit", "", "console: Quits gracefully"); + con_hook_command(&c_die, "die", "", "console: Quits ungracefully"); + + /* + con_hook_int(&(gfx_options.buffer_pics_nr), "buffer_pics_nr", + "Number of pics to buffer in LRU storage\n"); + con_hook_int(&(gfx_options.pic0_dither_mode), "pic0_dither_mode", + "Mode to use for pic0 dithering\n"); + con_hook_int(&(gfx_options.pic0_dither_pattern), "pic0_dither_pattern", + "Pattern to use for pic0 dithering\n"); + con_hook_int(&(gfx_options.pic0_unscaled), "pic0_unscaled", + "Whether pic0 should be drawn unscaled\n"); + con_hook_int(&(gfx_options.dirty_frames), "dirty_frames", + "Dirty frames management\n"); + */ + con_hook_int(&gfx_crossblit_alpha_threshold, "alpha_threshold", + "Alpha threshold for crossblitting\n"); + con_hook_int(&sci0_palette, "sci0_palette", + "SCI0 palette- 0: EGA, 1:AGI/Amiga, 2:Grayscale\n"); + con_hook_int(&sci01_priority_table_flags, "sci01_priority_table_flags", + "SCI01 priority table debugging flags: 1:Disable, 2:Print on change\n"); + + con_passthrough = 1; /* enables all sciprintf data to be sent to stdout */ +} + +static int +init_gamestate(state_t *gamestate, sci_version_t version) +{ + int errc; + + if ((errc = script_init_engine(gamestate, version))) { /* Initialize game state */ + int recovered = 0; + + if (errc == SCI_ERROR_INVALID_SCRIPT_VERSION) { + int tversion = SCI_VERSION_FTU_NEW_SCRIPT_HEADER - ((version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER)? 0 : 1); + + while (!recovered && tversion) { + printf("Trying version %d.%03x.%03d instead\n", SCI_VERSION_MAJOR(tversion), + SCI_VERSION_MINOR(tversion), SCI_VERSION_PATCHLEVEL(tversion)); + + errc = script_init_engine(gamestate, tversion); + + if ((recovered = !errc)) + version = tversion; + + if (errc != SCI_ERROR_INVALID_SCRIPT_VERSION) + break; + + switch (tversion) { + + case SCI_VERSION_FTU_NEW_SCRIPT_HEADER - 1: + if (version >= SCI_VERSION_FTU_NEW_SCRIPT_HEADER) + tversion = 0; + else + tversion = SCI_VERSION_FTU_NEW_SCRIPT_HEADER; + break; + + case SCI_VERSION_FTU_NEW_SCRIPT_HEADER: + tversion = 0; + break; + } + } + if (recovered) + printf("Success.\n"); + } + + if (!recovered) { + fprintf(stderr,"Script initialization failed. Aborting...\n"); + return 1; + } + } + return 0; +} + +static void +detect_versions(sci_version_t *version, int *res_version) +{ + sci_version_t exe_version; + sci_version_t hash_version; + int hash_res_version; + guint32 code; + int got_exe_version; + const char *game_name; + + sciprintf("Detecting interpreter and resource versions...\n"); + + got_exe_version = !version_detect_from_executable(&exe_version); + + if (got_exe_version) { + sciprintf("Interpreter version: %d.%03d.%03d (by executable scan)\n", + SCI_VERSION_MAJOR(exe_version), + SCI_VERSION_MINOR(exe_version), + SCI_VERSION_PATCHLEVEL(exe_version)); + + if (SCI_VERSION_MAJOR(exe_version) >= 1) { + sciprintf("FIXME: Implement version mapping (results of executable scan ignored)\n"); + got_exe_version = 0; + } + + } + + game_name = version_guess_from_hashcode(&hash_version, &hash_res_version, &code); + + if (game_name) { + sciprintf("Interpreter version: %d.%03d.%03d (by hash code %08X)\n", + SCI_VERSION_MAJOR(hash_version), + SCI_VERSION_MINOR(hash_version), + SCI_VERSION_PATCHLEVEL(hash_version), code); + if (got_exe_version && exe_version != hash_version) + sciprintf("UNEXPECTED INCONSISTENCY: Hash code %08X indicates interpreter version\n" + " %d.%03d.%03d, but analysis of the executable yields %d.%03d.%03d (for game\n" + " '%s'). Please report this!\n", + code, + SCI_VERSION_MAJOR(hash_version), + SCI_VERSION_MINOR(hash_version), + SCI_VERSION_PATCHLEVEL(hash_version), + SCI_VERSION_MAJOR(exe_version), + SCI_VERSION_MINOR(exe_version), + SCI_VERSION_PATCHLEVEL(exe_version), game_name); + + if (hash_res_version != SCI_VERSION_AUTODETECT) + sciprintf("Resource version: %d (by hash code)\n", hash_res_version); + + sciprintf("Game identified as '%s'\n", game_name); + } else { + sciprintf("Could not identify game by hash code: %08X\n", code); + + if (got_exe_version) + sciprintf("Please report the preceding two lines and the name of the game you were trying\n" + "to run to the FreeSCI development team to help other users!\n", + code); + } + + if (game_name) + *version = hash_version; + else if (got_exe_version) + *version = exe_version; + else + *version = 0; + + if (game_name) + *res_version = hash_res_version; + else + *res_version = SCI_VERSION_AUTODETECT; + + if (*version) + sciprintf("Using interpreter version %d.%03d.%03d\n", + SCI_VERSION_MAJOR(*version), + SCI_VERSION_MINOR(*version), + SCI_VERSION_PATCHLEVEL(*version)); + + if (*res_version != SCI_VERSION_AUTODETECT) + sciprintf("Using resource version %d\n", *res_version); +} + +int +main_(const char* gamedir) +{ + resource_mgr_t *resmgr; + + init_console(); /* So we can get any output */ + + char startdir[MAXPATHLEN+1] = ""; + getcwd(startdir, MAXPATHLEN); + script_debug_flag = 0; + + if (chdir(gamedir)) { + printf ("Error changing to game directory '%s'\n", gamedir); + exit(1); + } + + sci_version_t version; + int res_version; + + detect_versions(&version, &res_version); + + char resource_dir[MAXPATHLEN+1] = ""; + getcwd(resource_dir, MAXPATHLEN); /* Store resource directory */ + + resmgr = scir_new_resource_manager(resource_dir, res_version, 1, 256*1024); + + if (!resmgr) { + printf("No resources found in '%s'.\nAborting...\n", + resource_dir); + exit(1); + } + + script_adjust_opcode_formats(resmgr->sci_version); + + chdir(startdir); + +#if 0 + printf("Mapping instruments to General Midi\n"); + + map_MIDI_instruments(resmgr); +#endif + + sciprintf("Imported FreeSCI, version "VERSION"\n"); + + state_t gamestate; + memset(&gamestate, 0, sizeof(state_t)); + gamestate.resmgr = resmgr; + gamestate.gfx_state = NULL; + + if (init_gamestate(&gamestate, version)) + return 1; + + + if (game_init(&gamestate)) { /* Initialize */ + fprintf(stderr,"Game initialization failed: Aborting...\n"); + return 1; + } + + /* Set the savegame dir */ + script_set_gamestate_save_dir(&gamestate, ConfMan.get("savepath").c_str()); + + // Originally, work_dir tried to be ~/.freesci/game_name + gamestate.work_dir = sci_strdup(ConfMan.get("savepath").c_str()); + gamestate.resource_dir = resource_dir; + gamestate.port_serial = 0; + gamestate.have_mouse_flag = 1; + gamestate.animation_delay = 5; + gamestate.animation_granularity = 4; + gfx_crossblit_alpha_threshold = 0x90; + + gfx_state_t gfx_state; + gfx_state.driver = &gfx_driver_scummvm; + gfx_state.version = resmgr->sci_version; + gamestate.gfx_state = &gfx_state; + +/**** Default config: */ + gfx_options_t gfx_options; + gfx_options.workarounds = 0; + gfx_options.buffer_pics_nr = 0; + gfx_options.correct_rendering = 1; + gfx_options.pic0_unscaled = 1; + gfx_options.pic0_dither_mode = GFXR_DITHER_MODE_D256; + gfx_options.pic0_dither_pattern = GFXR_DITHER_PATTERN_SCALED; + gfx_options.pic0_brush_mode = GFX_BRUSH_MODE_RANDOM_ELLIPSES; + gfx_options.pic0_line_mode = GFX_LINE_MODE_CORRECT; + gfx_options.cursor_xlate_filter = GFX_XLATE_FILTER_NONE; + gfx_options.view_xlate_filter = GFX_XLATE_FILTER_NONE; + gfx_options.pic_xlate_filter = GFX_XLATE_FILTER_NONE; + gfx_options.text_xlate_filter = GFX_XLATE_FILTER_NONE; + gfx_options.dirty_frames = GFXOP_DIRTY_FRAMES_CLUSTERS; + gfx_options.pic0_antialiasing = GFXR_ANTIALIASING_NONE; + gfx_options.pic_port_bounds = gfx_rect(0,10,320,190); + for (int i = 0; i < GFX_RESOURCE_TYPES_NR; i++) { + gfx_options.res_conf.assign[i] = NULL; + gfx_options.res_conf.mod[i] = NULL; + } +/**** Default config ends */ + + if (gfxop_init_default(&gfx_state, &gfx_options, resmgr)) { + fprintf(stderr,"Graphics initialization failed. Aborting...\n"); + return 1; + } + + if (game_init_graphics(&gamestate)) { /* Init interpreter graphics */ + fprintf(stderr,"Game initialization failed: Error in GFX subsystem. Aborting...\n"); + return 1; + } + + if (game_init_sound(&gamestate, 0)) { + fprintf(stderr,"Game initialization failed: Error in sound subsystem. Aborting...\n"); + return 1; + } + + printf("Emulating SCI version %d.%03d.%03d\n", + SCI_VERSION_MAJOR(gamestate.version), + SCI_VERSION_MINOR(gamestate.version), + SCI_VERSION_PATCHLEVEL(gamestate.version)); + + state_t *tmp = &gamestate; + game_run(&tmp); /* Run the game */ + + game_exit(&gamestate); + script_free_engine(&gamestate); /* Uninitialize game state */ + script_free_breakpoints(&gamestate); + sci_free(gamestate.work_dir); + + scir_free_resource_manager(resmgr); + + close_console_file(); + + chdir (startdir); /* ? */ + + gfxop_exit(&gfx_state); + + return 0; +} + +SciEngine::SciEngine(OSystem *syst, const SciGameDescription *desc) + : 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(). + + // However this is the place to specify all default directories + //File::addDefaultDirectory(_gameDataPath + "sound/"); + //printf("%s\n", _gameDataPath.c_str()); + + // Set up the engine specific debug levels + //Common::addSpecialDebugLevel(SCI_DEBUG_RESOURCES, "resources", "Debug the resources loading"); + + printf("SciEngine::SciEngine\n"); +} + +SciEngine::~SciEngine() { + // Dispose your resources here + printf("SciEngine::~SciEngine\n"); + + // Remove all of our debug levels here + //Common::clearAllSpecialDebugLevels(); +} + +int SciEngine::init(void) { + initCommonGFX(false); + _system->beginGFXTransaction(); + //_system->initSize(640, 400),; + _system->initSize(320, 200); + _system->endGFXTransaction(); + + //GUIErrorMessage("lalalal asfa w4 haha hreh au ugetEventManager(); + while (!end) { + Common::Event ev; + if (em->pollEvent(ev)) { + if (ev.type == Common::EVENT_KEYDOWN) { + end = true; + } + } + _system->delayMillis(10); + } */ + + main_(_gameDataPath.c_str()); + + return 0; +} + +//} // End of namespace Sci diff --git a/engines/sci/scummvm/scummvm_engine.h b/engines/sci/scummvm/scummvm_engine.h new file mode 100644 index 0000000000..dc614f6d55 --- /dev/null +++ b/engines/sci/scummvm/scummvm_engine.h @@ -0,0 +1,54 @@ +#ifndef SCI_H +#define SCI_H + +#include "engines/engine.h" +#include "gui/debugger.h" + +//namespace Sci { + +// our engine debug levels +enum { + SCI_DEBUG_RESOURCES = 1 << 0, + SCI_DEBUG_todo = 1 << 1 +}; + +struct GameFlags { + //int gameType; + //int gameId; + //uint32 features; + // SCI Version + // Resource Map Version + // etc... +}; + +struct SciGameDescription { + Common::ADGameDescription desc; + GameFlags flags; +}; + +//class Console; + +class SciEngine : public Engine { + public: + SciEngine(OSystem *syst, const SciGameDescription *desc); + ~SciEngine(); + + virtual int init(void); + virtual int go(void); + + private: + //Console *_console; +}; + +/* +// Example console +class Console : public GUI::Debugger { + public: + //Console(SciEngine *vm); + //virtual ~Console(void); +}; +*/ + +//} // End of namespace Sci + +#endif // SCI_H diff --git a/engines/sci/sfx/Makefile.am b/engines/sci/sfx/Makefile.am new file mode 100644 index 0000000000..b085a7c917 --- /dev/null +++ b/engines/sci/sfx/Makefile.am @@ -0,0 +1,27 @@ +SUBDIRS = seq timer player device mixer pcm_device softseq +INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@ +EXTRA_DIST = timetest.c adlib.h device.h mixer.h sequencer.h softseq.h +noinst_LIBRARIES = libscisound.a libscisoundlib.a +libscisound_a_SOURCES = iterator.c songlib.c core.c pcm-iterator.c +libscisoundlib_a_SOURCES = time.c adlib.c + +bin_PROGRAMS = test-iterator +test_iterator_SOURCE = test-iterator.c +test_iterator_LDFLAGS = @SCIV_LDFLAGS@ +LDADD = \ + libscisound.a \ + player/libsciplayer.a \ + seq/libsciseq.a \ + timer/libscitimer.a \ + pcm_device/libscipcm.a \ + mixer/libscimixer.a \ + softseq/libscisoftseq.a \ + libscisoundlib.a \ + device/libscisounddevice.a \ + ../scicore/libscicore.a \ + @LIB_M@ + + +TESTS = test-iterator + + diff --git a/engines/sci/sfx/adlib.c b/engines/sci/sfx/adlib.c new file mode 100644 index 0000000000..dab3902770 --- /dev/null +++ b/engines/sci/sfx/adlib.c @@ -0,0 +1,66 @@ +/*************************************************************************** + adlib.c Copyright (C) 2001 Solomon Peachy + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#include "adlib.h" + +adlib_instr adlib_sbi[96]; + +void +make_sbi(adlib_def *one, guint8 *buffer) +{ + memset(buffer, 0, sizeof(adlib_instr)); + +#if 0 + printf ("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x ", one->keyscale1, one->freqmod1, one->feedback1, one->attackrate1, one->sustainvol1, one->envelope1, one->decayrate1, one->releaserate1, one->volume1, one->ampmod1, one->vibrato1, one->keybdscale1, one->algorithm1, one->waveform1); + + printf (" %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x ", one->keyscale2, one->freqmod2, one->feedback2, one->attackrate2, one->sustainvol2, one->envelope2, one->decayrate2, one->releaserate2, one->volume2, one->ampmod2, one->vibrato2, one->keybdscale2, one->algorithm2, one->waveform2); + + printf("\n"); +#endif + + buffer[0] |= ((one->ampmod1 & 0x1) << 7); + buffer[0] |= ((one->vibrato1 & 0x1) << 6); + buffer[0] |= ((one->envelope1 & 0x1) << 5); + buffer[0] |= ((one->keybdscale1 & 0x1) << 4); + buffer[0] |= (one->freqmod1 & 0xf); + buffer[1] |= ((one->ampmod2 & 0x1) << 7); + buffer[1] |= ((one->vibrato2 & 0x1) << 6); + buffer[1] |= ((one->envelope2 & 0x1) << 5); + buffer[1] |= ((one->keybdscale2 & 0x1) << 4); + buffer[1] |= (one->freqmod2 & 0xf); + buffer[2] |= ((one->keyscale1 & 0x3) << 6); + buffer[2] |= (one->volume1 & 0x3f); + buffer[3] |= ((one->keyscale2 & 0x3) << 6); + buffer[3] |= (one->volume2 & 0x3f); + buffer[4] |= ((one->attackrate1 & 0xf) << 4); + buffer[4] |= (one->decayrate1 & 0xf); + buffer[5] |= ((one->attackrate2 & 0xf) << 4); + buffer[5] |= (one->decayrate2 & 0xf); + buffer[6] |= ((one->sustainvol1 & 0xf) << 4); + buffer[6] |= (one->releaserate1 & 0xf); + buffer[7] |= ((one->sustainvol2 & 0xf) << 4); + buffer[7] |= (one->releaserate2 & 0xf); + buffer[8] |= (one->waveform1 & 0x3); + buffer[9] |= (one->waveform2 & 0x3); + + buffer[10] |= ((one->feedback1 & 0x7) << 1); + buffer[10] |= (1-(one->algorithm1 & 0x1)); + + return; +} diff --git a/engines/sci/sfx/adlib.h b/engines/sci/sfx/adlib.h new file mode 100644 index 0000000000..214b7100b7 --- /dev/null +++ b/engines/sci/sfx/adlib.h @@ -0,0 +1,69 @@ +/*************************************************************************** + sfx_adlib.h, from + midi_device.h Copyright (C) 2001 Solomon Peachy + Copytight (C) 2002..04 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + + +#ifndef _SFX_ADLIB_H_ +#define _SFX_ADLIB_H_ + +#include + + +#define ADLIB_VOICES 12 + +typedef struct _sci_adlib_def { + guint8 keyscale1; /* 0-3 !*/ + guint8 freqmod1; /* 0-15 !*/ + guint8 feedback1; /* 0-7 !*/ + guint8 attackrate1; /* 0-15 !*/ + guint8 sustainvol1; /* 0-15 !*/ + guint8 envelope1; /* t/f !*/ + guint8 decayrate1; /* 0-15 !*/ + guint8 releaserate1; /* 0-15 !*/ + guint8 volume1; /* 0-63 !*/ + guint8 ampmod1; /* t/f !*/ + guint8 vibrato1; /* t/f !*/ + guint8 keybdscale1; /* t/f !*/ + guint8 algorithm1; /* 0,1 REVERSED */ + guint8 keyscale2; /* 0-3 !*/ + guint8 freqmod2; /* 0-15 !*/ + guint8 feedback2; /* 0-7 UNUSED */ + guint8 attackrate2; /* 0-15 !*/ + guint8 sustainvol2; /* 0-15 !*/ + guint8 envelope2; /* t/f !*/ + guint8 decayrate2; /* 0-15 !*/ + guint8 releaserate2; /* 0-15 !*/ + guint8 volume2; /* 0-63 !*/ + guint8 ampmod2; /* t/f !*/ + guint8 vibrato2; /* t/f !*/ + guint8 keybdscale2; /* t/f !*/ + guint8 algorithm2; /* 0,1 UNUSED */ + guint8 waveform1; /* 0-3 !*/ + guint8 waveform2; /* 0-3 !*/ +} adlib_def; + +typedef unsigned char adlib_instr[12]; + +extern adlib_instr adlib_sbi[96]; + +void make_sbi(adlib_def *one, guint8 *buffer); +/* Converts a raw SCI adlib instrument into the adlib register format. */ + +#endif /* _SFX_ADLIB_H_ */ diff --git a/engines/sci/sfx/core.c b/engines/sci/sfx/core.c new file mode 100644 index 0000000000..9d55ad6bbf --- /dev/null +++ b/engines/sci/sfx/core.c @@ -0,0 +1,938 @@ +/*************************************************************************** + core.c Copyright (C) 2002 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Sound subsystem core: Event handler, sound player dispatching */ + +#include +#include +#include +#include +#include "mixer.h" +#include + + +/*#define DEBUG_SONG_API*/ +/*#define DEBUG_CUES*/ +#ifdef DEBUG_CUES +int sciprintf(char *msg, ...); +#endif + +static sfx_player_t *player = NULL; +sfx_pcm_mixer_t *mixer = NULL; +static sfx_pcm_device_t *pcm_device = NULL; +static sfx_timer_t *timer = NULL; + +#define MILLION 1000000 + +int +sfx_pcm_available() +{ + return (pcm_device != NULL); +} + +void +sfx_reset_player(void) +{ + if (player) + player->stop(); +} + +tell_synth_func * +sfx_get_player_tell_func(void) +{ + if (player) + return player->tell_synth; + else + return NULL; +} + +int +sfx_get_player_polyphony(void) +{ + if (player) + return player->polyphony; + else + return 0; +} + +static long +time_minus(GTimeVal t1, GTimeVal t2) +{ + return (t1.tv_sec - t2.tv_sec) * MILLION + + (t1.tv_usec - t2.tv_usec); +} + +static GTimeVal +time_plus(GTimeVal t1, long delta) +{ + if (delta > 0) + t1.tv_usec += delta % MILLION; + else + t1.tv_usec -= (-delta) % MILLION; + + t1.tv_sec += delta / MILLION; + + if (t1.tv_usec > MILLION) { + t1.tv_sec++; + t1.tv_usec -= MILLION; + } + + return t1; +} + + +static void +_freeze_time(sfx_state_t *self) +{ + /* Freezes the top song delay time */ + GTimeVal ctime; + long delta; + + song_t *song = self->song; + sci_get_current_time(&ctime); + + while (song) { + delta = time_minus(song->wakeup_time, ctime); + if (delta < 0) + delta = 0; + + song->delay = delta; + + song = song->next_playing; + } +} + + +static void +_dump_playing_list(sfx_state_t *self, char *msg) +{ + song_t *song = self->song; + + fprintf(stderr, "[] Song list : [ "); + song = *(self->songlib.lib); + while (song) { + fprintf(stderr, "%08lx:%d ", song->handle, song->status); + song = song->next_playing; + } + fprintf(stderr, "]\n"); + + fprintf(stderr, "[] Play list (%s) : [ " , msg); + + while (song) { + fprintf(stderr, "%08lx ", song->handle); + song = song->next_playing; + } + + fprintf(stderr, "]\n"); +} + +static void +_dump_songs(sfx_state_t *self) +{ +#if 0 + song_t *song = self->song; + + fprintf(stderr, "Cue iterators:\n"); + song = *(self->songlib.lib); + while (song) { + fprintf(stderr, " **\tHandle %08x (p%d): status %d\n", + song->handle, song->priority, song->status); + SIMSG_SEND(song->it, SIMSG_PRINT(1)); + song = song->next; + } + + if (player) { + fprintf(stderr, "Audio iterator:\n"); + player->iterator_message(songit_make_message(0, SIMSG_PRINT(1))); + } +#endif +} + + +static void +_thaw_time(sfx_state_t *self) +{ + /* inverse of _freeze_time() */ + GTimeVal ctime; + song_t *song = self->song; + + sci_get_current_time(&ctime); + + while (song) { + song->wakeup_time = time_plus(ctime, song->delay); + + song = song->next_playing; + } +} + +static int +is_playing(sfx_state_t *self, song_t *song) +{ + song_t *playing_song = self->song; + +/* _dump_playing_list(self, "is-playing");*/ + + while (playing_song) { + if (playing_song == song) + return 1; + playing_song = playing_song->next_playing; + } + return 0; +} + +static void +_sfx_set_song_status(sfx_state_t *self, song_t *song, int status) +{ + switch (status) { + + case SOUND_STATUS_STOPPED: + /* Reset */ + song->it->init(song->it); + break; + + case SOUND_STATUS_SUSPENDED: + case SOUND_STATUS_WAITING: + + if (song->status == SOUND_STATUS_PLAYING) { + /* Update delay, set wakeup_time */ + GTimeVal time; + long delta; + sci_get_current_time(&time); + delta = time_minus(time, song->wakeup_time); + + song->delay -= delta; + song->wakeup_time = time; + } + if (status == SOUND_STATUS_SUSPENDED) + break; + + /* otherwise... */ + + case SOUND_STATUS_PLAYING: + if (song->status == SOUND_STATUS_STOPPED) + /* Starting anew */ + sci_get_current_time(&song->wakeup_time); + + if (is_playing(self, song)) + status = SOUND_STATUS_PLAYING; + else + status = SOUND_STATUS_WAITING; + break; + + default: + fprintf(stderr, "%s L%d: Attempt to set invalid song" + " state %d!\n", __FILE__, __LINE__, status); + return; + + } + song->status = status; +} + +/* Update internal state iff only one song may be played */ +static void +_update_single_song(sfx_state_t *self) +{ + song_t *newsong = song_lib_find_active(self->songlib); + + if (newsong != self->song) { + + _freeze_time(self); /* Store song delay time */ + + if (player) + player->stop(); + + if (newsong) { + if (!newsong->it) + return; /* Restore in progress and not ready for this yet */ + + /* Change song */ + if (newsong->status == SOUND_STATUS_WAITING) + _sfx_set_song_status(self, newsong, + SOUND_STATUS_PLAYING); + + /* Change instrument mappings */ + } else { + /* Turn off sound */ + } + if (self->song) { + if (self->song->status == SOUND_STATUS_PLAYING) + _sfx_set_song_status(self, newsong, + SOUND_STATUS_WAITING); + } + + if (self->debug & SFX_DEBUG_SONGS) { + sciprintf("[SFX] Changing active song:"); + if (!self->song) + sciprintf(" New song:"); + else + sciprintf(" pausing %08lx, now playing", + self->song->handle); + + if (newsong) + sciprintf(" %08lx\n", newsong->handle); + else + sciprintf(" none\n"); + } + + + self->song = newsong; + _thaw_time(self); /* Recover song delay time */ + + if (newsong && player) { + song_iterator_t *clonesong + = songit_clone(newsong->it, newsong->delay); + + player->add_iterator(clonesong, + newsong->wakeup_time); + } + } +} + + +static void +_update_multi_song(sfx_state_t *self) +{ + song_t *oldfirst = self->song; + song_t *oldseeker; + song_t *newsong = song_lib_find_active(self->songlib); + song_t *newseeker; + song_t not_playing_anymore; /* Dummy object, referenced by + ** songs which are no longer + ** active. */ + GTimeVal tv; + sci_get_current_time(&tv); +/* _dump_playing_list(self, "before");*/ + _freeze_time(self); /* Store song delay time */ + + for (newseeker = newsong; newseeker; + newseeker = newseeker->next_playing) { + if (!newseeker->it) + return; /* Restore in progress and not ready for this yet */ + } + + /* First, put all old songs into the 'stopping' list and + ** mark their 'next-playing' as not_playing_anymore. */ + for (oldseeker = oldfirst; oldseeker; + oldseeker = oldseeker->next_stopping) { + oldseeker->next_stopping = oldseeker->next_playing; + oldseeker->next_playing = ¬_playing_anymore; + + if (oldseeker == oldseeker->next_playing) { BREAKPOINT(); } + } + + /* Second, re-generate the new song queue. */ + for (newseeker = newsong; newseeker; + newseeker = newseeker->next_playing) { + newseeker->next_playing + = song_lib_find_next_active(self->songlib, + newseeker); + + if (newseeker == newseeker->next_playing) { BREAKPOINT(); } + } + /* We now need to update the currently playing song list, because we're + ** going to use some functions that require this list to be in a sane + ** state (particularly is_playing(), indirectly */ + self->song = newsong; + + /* Third, stop all old songs */ + for (oldseeker = oldfirst; oldseeker; + oldseeker = oldseeker->next_stopping) + if (oldseeker->next_playing == ¬_playing_anymore) { + _sfx_set_song_status(self, oldseeker, + SOUND_STATUS_SUSPENDED); + if (self->debug & SFX_DEBUG_SONGS) { + sciprintf("[SFX] Stopping song %lx\n", oldseeker->handle); + } + if (player && oldseeker->it) + player->iterator_message + (songit_make_message(oldseeker->it->ID, SIMSG_STOP)); + oldseeker->next_playing = NULL; /* Clear this pointer; we don't need the tag anymore */ + } + + for (newseeker = newsong; newseeker; + newseeker = newseeker->next_playing) { + if (newseeker->status != SOUND_STATUS_PLAYING && player) { + if (self->debug & SFX_DEBUG_SONGS) + sciprintf("[SFX] Adding song %lx\n", newseeker->it->ID); + + player->add_iterator(songit_clone(newseeker->it, + newseeker->delay), + tv); + } + _sfx_set_song_status(self, newseeker, + SOUND_STATUS_PLAYING); + } + + self->song = newsong; + _thaw_time(self); +/* _dump_playing_list(self, "after");*/ +} + +/* Update internal state */ +static void +_update(sfx_state_t *self) +{ + if (self->flags & SFX_STATE_FLAG_MULTIPLAY) + _update_multi_song(self); + else + _update_single_song(self); +} + + +static int _sfx_timer_active = 0; /* Timer toggle */ + +int +sfx_play_iterator_pcm(song_iterator_t *it, song_handle_t handle) +{ +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Playing PCM: %08lx\n", handle); +#endif + if (mixer) { + sfx_pcm_feed_t *newfeed = it->get_pcm_feed(it); + if (newfeed) { + newfeed->debug_nr = (int) handle; + mixer->subscribe(mixer, newfeed); + return 1; + } + } + return 0; +} + +static void +_sfx_timer_callback(void *data) +{ + if (_sfx_timer_active) { + /* First run the player, to give it a chance to fill + ** the audio buffer */ + + if (player) + player->maintenance(); + + if (mixer) + mixer->process(mixer); + } +} + +void +sfx_init(sfx_state_t *self, resource_mgr_t *resmgr, int flags) +{ + song_lib_init(&self->songlib); + self->song = NULL; + self->flags = flags; + self->debug = 0; /* Disable all debugging by default */ + + if (flags & SFX_STATE_FLAG_NOSOUND) { + mixer = NULL; + pcm_device = NULL; + player = NULL; + sciprintf("[SFX] Sound disabled.\n"); + return; + } + + mixer = sfx_pcm_find_mixer(NULL); + pcm_device = sfx_pcm_find_device(NULL); + player = sfx_find_player(NULL); + + +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Initialising: flags=%x\n", flags); +#endif + + /*------------------*/ + /* Initialise timer */ + /*------------------*/ + + if (pcm_device || player->maintenance) { + if (pcm_device && pcm_device->timer) + timer = pcm_device->timer; + else + timer = sfx_find_timer(NULL); + + if (!timer) { + fprintf(stderr, "[SFX] " __FILE__": Could not find timing mechanism\n"); + fprintf(stderr, "[SFX] Disabled sound support\n"); + pcm_device = NULL; + player = NULL; + mixer = NULL; + return; + } + + if (timer->init(_sfx_timer_callback, NULL)) { + fprintf(stderr, "[SFX] " __FILE__": Timer failed to initialize\n"); + fprintf(stderr, "[SFX] Disabled sound support\n"); + timer = NULL; + pcm_device = NULL; + player = NULL; + mixer = NULL; + return; + } + + sciprintf("[SFX] Initialised timer '%s', v%s\n", + timer->name, timer->version); + } /* With no PCM device and no player, we don't need a timer */ + + /*----------------*/ + /* Initialise PCM */ + /*----------------*/ + + if (!pcm_device) { + sciprintf("[SFX] No PCM device found, disabling PCM support\n"); + mixer = NULL; + } else { + if (pcm_device->init(pcm_device)) { + sciprintf("[SFX] Failed to open PCM device, disabling PCM support\n"); + mixer = NULL; + pcm_device = NULL; + } else { + if (mixer->init(mixer, pcm_device)) { + sciprintf("[SFX] Failed to initialise PCM mixer; disabling PCM support\n"); + mixer = NULL; + pcm_device->exit(pcm_device); + pcm_device = NULL; + } + } + } + + /*-------------------*/ + /* Initialise player */ + /*-------------------*/ + + if (!resmgr) { + sciprintf("[SFX] Warning: No resource manager present, cannot initialise player\n"); + player = NULL; + } else if (player->init(resmgr, timer? timer->delay_ms : 0)) { + sciprintf("[SFX] Song player '%s' reported error, disabled\n", player->name); + player = NULL; + } + + if (!player) + sciprintf("[SFX] No song player found\n"); + else + sciprintf("[SFX] Using song player '%s', v%s\n", player->name, player->version); + + _sfx_timer_active = 1; +} + +void +sfx_exit(sfx_state_t *self) +{ + _sfx_timer_active = 0; +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Uninitialising\n"); +#endif + + song_lib_free(self->songlib); + + if (pcm_device) { + pcm_device->exit(pcm_device); + pcm_device = NULL; + } + if (timer && timer->exit()) + fprintf(stderr, "[SFX] Timer reported error on exit\n"); + + /* WARNING: The mixer may hold feeds from the + ** player, so we must stop the mixer BEFORE + ** stopping the player. */ + if (mixer) { + mixer->exit(mixer); + mixer = NULL; + } + + if (player) + /* See above: This must happen AFTER stopping the mixer */ + player->exit(); + +} + +static inline int +time_le(GTimeVal a, GTimeVal b) +{ + return time_minus(a, b) <= 0; +} + +void +sfx_suspend(sfx_state_t *self, int suspend) +{ +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Suspending? = %d\n", suspend); +#endif + if (suspend && (!self->suspended)) { + /* suspend */ + + _freeze_time(self); + if (player) + player->pause(); + /* Suspend song player */ + + } else if (!suspend && (self->suspended)) { + /* unsuspend */ + + _thaw_time(self); + if (player) + player->resume(); + + /* Unsuspend song player */ + } + + self->suspended = suspend; +} + +int +sfx_poll(sfx_state_t *self, song_handle_t *handle, int *cue) +/* Polls the sound server for cues etc. +** Returns : (int) 0 if the cue queue is empty, SI_LOOP, SI_CUE, or SI_FINISHED otherwise +** (song_handle_t) *handle: The affected handle +** (int) *cue: The sound cue number (if SI_CUE) +*/ +{ + if (!self->song) + return 0; /* No milk today */ + + *handle = self->song->handle; + +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Polling any (%08lx)\n", *handle); +#endif + return sfx_poll_specific(self, *handle, cue); +} + +int +sfx_poll_specific(sfx_state_t *self, song_handle_t handle, int *cue) +{ + GTimeVal ctime; + song_t *song = self->song; + + sci_get_current_time(&ctime); + + while (song && song->handle != handle) + song = song->next_playing; + + if (!song) + return 0; /* Song not playing */ + + if (self->debug & SFX_DEBUG_CUES) { + fprintf(stderr, "[SFX:CUE] Polled song %08lx ", handle); + } + + while (1) { + unsigned char buf[8]; + int result; + + if (!time_le(song->wakeup_time, ctime)) + return 0; /* Patience, young hacker! */ + result = songit_next(&(song->it), buf, cue, + IT_READER_MASK_ALL); + + switch (result) { + + case SI_FINISHED: + _sfx_set_song_status(self, song, + SOUND_STATUS_STOPPED); + _update(self); + /* ...fall through... */ + case SI_LOOP: + case SI_RELATIVE_CUE: + case SI_ABSOLUTE_CUE: + if (self->debug & SFX_DEBUG_CUES) { + sciprintf(" => "); + + if (result == SI_FINISHED) + sciprintf("finished\n"); + else { + if (result == SI_LOOP) + sciprintf("Loop: "); + else + sciprintf("Cue: "); + + sciprintf("%d (0x%x)", *cue, *cue); + } + } + return result; + + default: + if (result > 0) + song->wakeup_time = + time_plus(song->wakeup_time, + result * SOUND_TICK); + /* Delay */ + break; + } + } + if (self->debug & SFX_DEBUG_CUES) { + fprintf(stderr, "\n"); + } +} + + +/*****************/ +/* Song basics */ +/*****************/ + +int +sfx_add_song(sfx_state_t *self, song_iterator_t *it, int priority, song_handle_t handle, int number) +{ + song_t *song = song_lib_find(self->songlib, handle); + +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Adding song: %08lx at %d, it=%p\n", handle, priority, it); +#endif + if (!it) { + fprintf(stderr, "[SFX] Attempt to add empty song with handle %08lx\n", handle); + return -1; + } + + it->init(it); + + /* If we're already playing this, stop it */ + /* Tell player to shut up */ + _dump_songs(self); + + if (player) + player->iterator_message(songit_make_message(handle, SIMSG_STOP)); + + if (song) { + _sfx_set_song_status(self, song, SOUND_STATUS_STOPPED); + + fprintf(stderr, "Overwriting old song (%08lx) ...\n", handle); + if (song->status == SOUND_STATUS_PLAYING + || song->status == SOUND_STATUS_SUSPENDED) { + fprintf(stderr, "Unexpected (error): Song %ld still playing/suspended (%d)\n", + handle, song->status); + songit_free(it); + return -1; + } else + song_lib_remove(self->songlib, handle); /* No duplicates */ + + } + + song = song_new(handle, it, priority); + song->resource_num = number; + song->hold = 0; + song->loops = 0; + sci_get_current_time(&song->wakeup_time); /* No need to delay */ + song_lib_add(self->songlib, song); + self->song = NULL; /* As above */ + _update(self); + + return 0; +} + + +void +sfx_remove_song(sfx_state_t *self, song_handle_t handle) +{ +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Removing song: %08lx\n", handle); +#endif + if (self->song && self->song->handle == handle) + self->song = NULL; + + song_lib_remove(self->songlib, handle); + _update(self); +} + + + +/**********************/ +/* Song modifications */ +/**********************/ + +#define ASSERT_SONG(s) if (!(s)) { fprintf(stderr, "Looking up song handle %08lx failed in %s, L%d\n", handle, __FILE__, __LINE__); return; } + +void +sfx_song_set_status(sfx_state_t *self, song_handle_t handle, int status) +{ + song_t *song = song_lib_find(self->songlib, handle); + ASSERT_SONG(song); +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Setting song status to %d" + " (0:stop, 1:play, 2:susp, 3:wait): %08lx\n", status, handle); +#endif + + _sfx_set_song_status(self, song, status); + + _update(self); +} + +void +sfx_song_set_fade(sfx_state_t *self, song_handle_t handle, + fade_params_t *params) +{ +#ifdef DEBUG_SONG_API + static const char *stopmsg[] = {"??? Should not happen", "Do not stop afterwards","Stop afterwards"}; +#endif + song_t *song = song_lib_find(self->songlib, handle); + + ASSERT_SONG(song); + +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Setting fade params of %08lx to " + "final volume %d in steps of %d per %d ticks. %s.\n", + handle, fade->final_volume, fade->step_size, fade->ticks_per_step, + stopmsg[fade->action]); +#endif + + SIMSG_SEND_FADE(song->it, params); + + _update(self); +} + +void +sfx_song_renice(sfx_state_t *self, song_handle_t handle, int priority) +{ + song_t *song = song_lib_find(self->songlib, handle); + ASSERT_SONG(song); +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Renicing song %08lx to %d\n", + handle, priority); +#endif + + song->priority = priority; + + _update(self); +} + +void +sfx_song_set_loops(sfx_state_t *self, song_handle_t handle, int loops) +{ + song_t *song = song_lib_find(self->songlib, handle); + song_iterator_message_t msg + = songit_make_message(handle, SIMSG_SET_LOOPS(loops)); + ASSERT_SONG(song); + +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Setting loops on %08lx to %d\n", + handle, loops); +#endif + songit_handle_message(&(song->it), msg); + song->loops = ((base_song_iterator_t *) song->it)->loops; + + if (player/* && player->send_iterator_message*/) + /* FIXME: The above should be optional! */ + player->iterator_message(msg); +} + +void +sfx_song_set_hold(sfx_state_t *self, song_handle_t handle, int hold) +{ + song_t *song = song_lib_find(self->songlib, handle); + song_iterator_message_t msg + = songit_make_message(handle, SIMSG_SET_HOLD(hold)); + ASSERT_SONG(song); + + song->hold = hold; +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Setting hold on %08lx to %d\n", + handle, loops); +#endif + songit_handle_message(&(song->it), msg); + + if (player/* && player->send_iterator_message*/) + /* FIXME: The above should be optional! */ + player->iterator_message(msg); +} + +/* Different from the one in iterator.c */ +static const int MIDI_cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0, + 3, 3, 0, 3, 2, 0, 3, 0}; + +static const song_handle_t midi_send_base = 0xffff0000; + +static song_handle_t midi_send_handle = 0xffff0000; + +int +sfx_send_midi(sfx_state_t *self, song_handle_t handle, int channel, + int command, int arg1, int arg2) +{ + byte buffer[5]; + tell_synth_func *tell = sfx_get_player_tell_func(); + + /* Yes, in that order. SCI channel mutes are actually done via + a counting semaphore. 0 means to decrement the counter, 1 + to increment it. */ + static char *channel_state[] = {"ON","OFF"}; + + if (command == 0xb0 && + arg1 == SCI_MIDI_CHANNEL_MUTE) + { + sciprintf("TODO: channel mute (channel %d %s)!\n", channel, + channel_state[arg2]); + /* We need to have a GET_PLAYMASK interface to use + here. SET_PLAYMASK we've got. + */ + return SFX_OK; + } + + buffer[0] = channel | command; /* No channel remapping yet */ + + switch (command) + { + case 0x80 : + case 0x90 : + case 0xb0 : + buffer[1] = arg1&0xff; + buffer[2] = arg2&0xff; + break; + case 0xc0 : + buffer[1] = arg1&0xff; + break; + case 0xe0 : + buffer[1] = (arg1&0x7f) | 0x80; + buffer[2] = (arg1&0xff00) >> 7; + break; + default: + sciprintf("Unexpected explicit MIDI command %02x\n", command); + return SFX_ERROR; + } + + if (tell) + tell(MIDI_cmdlen[command >> 4], buffer); + return SFX_OK; +} + +int +sfx_get_volume(sfx_state_t *self) +{ + fprintf(stderr, "FIXME: Implement volume\n"); + return 0; +} + +void +sfx_set_volume(sfx_state_t *self, int volume) +{ + fprintf(stderr, "FIXME: Implement volume\n"); +} + +void +sfx_all_stop(sfx_state_t *self) +{ +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] All stop\n"); +#endif + + song_lib_free(self->songlib); + _update(self); +} diff --git a/engines/sci/sfx/device.h b/engines/sci/sfx/device.h new file mode 100644 index 0000000000..49bc017c7b --- /dev/null +++ b/engines/sci/sfx/device.h @@ -0,0 +1,117 @@ +/*************************************************************************** + sfx_device.h Copyright (C) 2003,04 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* song player structure */ + +#ifndef _SFX_DEVICE_H +#define _SFX_DEVICE_H + +/* Various types of resources */ +#define SFX_DEVICE_NONE 0 +#define SFX_DEVICE_MIDI 1 /* midi writer */ +#define SFX_DEVICE_OPL2 2 /* OPL/2 sequencer */ + +struct _midi_device { + const char *name; + + int (*init)(struct _midi_device *self); + /* Initializes the device + ** Parameters: (midi_device_t *) self: Self reference + ** Returns : (int) SFX_OK on success, SFX_ERROR if the device could not be + ** opened + */ + + int (*set_option)(struct _midi_device *self, char *name, char *value); + /* Sets an option for the device + ** Parameters: (char *) name: Name of the option to set + ** (char *) value: Value of the option to set + ** Returns : (int) SFX_OK on success, SFX_ERROR otherwise (unsupported option) + */ +}; + +#define MIDI_WRITER_BODY \ + char *name; /* Name description of the device */ \ + \ + int (*init)(struct _midi_writer *self); \ + /* Initializes the writer \ + ** Parameters: (midi_writer_t *) self: Self reference \ + ** Returns : (int) SFX_OK on success, SFX_ERROR if the device could not be \ + ** opened \ + */ \ + \ + int (*set_option)(struct _midi_writer *self, char *name, char *value); \ + /* Sets an option for the writer \ + ** Parameters: (char *) name: Name of the option to set \ + ** (char *) value: Value of the option to set \ + ** Returns : (int) SFX_OK on success, SFX_ERROR otherwise (unsupported option) \ + */ \ + \ + int (*write)(struct _midi_writer *self, unsigned char *buf, int len); \ + /* Writes some bytes to the MIDI stream \ + ** Parameters: (char *) buf: The buffer to write \ + ** (int) len: Number of bytes to write \ + ** Returns : (int) SFX_OK on success, SFX_ERROR on failure \ + ** No delta time is expected here. \ + */ \ + \ + void (*delay)(struct _midi_writer *self, int ticks); \ + /* Introduces an explicit delay \ + ** Parameters: (int) ticks: Number of 60 Hz ticks to sleep \ + */ \ + \ + void (*flush)(struct _midi_writer *self); /* May be NULL */ \ + /* Flushes the MIDI file descriptor \ + ** Parameters: (midi_writer_t *) self: Self reference \ + */ \ + \ + void (*reset_timer)(struct _midi_writer *self); \ + /* Resets the timer associated with this device \ + ** Parameters: (midi_writer_t *) self: Self reference \ + ** This function makes sure that a subsequent write would have effect \ + ** immediately, and any delay() would be relative to the point in time \ + ** this function was invoked at. \ + */ \ + \ + void (*close)(struct _midi_writer *self); \ + /* Closes the associated MIDI device \ + ** Parameters: (midi_writer_t *) self: Self reference \ + */ + +typedef struct _midi_writer { + MIDI_WRITER_BODY +} midi_writer_t; + + +void * +sfx_find_device(int type, char *name); +/* Looks up a device by name +** Parameters: (int) type: Device type to look up +** (char *) name: Comma-separated list of devices to choose from +** (in the order specified), or NULL for default +** Returns : (void *) The device requested, or NULL if no match was found +*/ + +#endif /* !_SFX_PLAYER_H */ diff --git a/engines/sci/sfx/device/Makefile.am b/engines/sci/sfx/device/Makefile.am new file mode 100644 index 0000000000..4a4f023251 --- /dev/null +++ b/engines/sci/sfx/device/Makefile.am @@ -0,0 +1,3 @@ +noinst_LIBRARIES = libscisounddevice.a +INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@ +libscisounddevice_a_SOURCES = devices.c alsa-midi.c unixraw-midi.c camd-midi.c diff --git a/engines/sci/sfx/device/alsa-midi.c b/engines/sci/sfx/device/alsa-midi.c new file mode 100644 index 0000000000..67ca995916 --- /dev/null +++ b/engines/sci/sfx/device/alsa-midi.c @@ -0,0 +1,227 @@ +/*************************************************************************** + alsa-midi.c Copyright (C) 2002 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include "../device.h" +#ifdef HAVE_ALSA + +#include + +#define SCI_ALSA_MIDI_VERSION "0.1" + +static snd_midi_event_t *parser = NULL; +static snd_seq_t *seq = NULL; +static int queue = -1; +static int delta = 0; +static int port_out = -1; +static int port_nr = 128; +static int subport_nr = 0; + +static const char *seq_name = "default"; + +static void +_set_tempo(void) +{ + int resolution = 60; + int tempo = 1; + snd_seq_queue_tempo_t *queue_tempo; + + snd_seq_queue_tempo_malloc(&queue_tempo); + + memset(queue_tempo, 0, snd_seq_queue_tempo_sizeof()); + snd_seq_queue_tempo_set_ppq(queue_tempo, resolution); + snd_seq_queue_tempo_set_tempo(queue_tempo, 1000000/tempo); + + snd_seq_set_queue_tempo(seq, queue, queue_tempo); + + snd_seq_queue_tempo_free(queue_tempo); + +#if 0 + int tempo = 1000000 / 60; + snd_seq_queue_tempo_t *queue_tempo; + + snd_seq_queue_tempo_malloc(&queue_tempo); + snd_seq_queue_tempo_set_tempo(queue_tempo, tempo); + snd_seq_queue_tempo_set_ppq(queue_tempo, 1); + snd_seq_set_queue_tempo(seq, queue, queue_tempo); + snd_seq_queue_tempo_free(queue_tempo); +#endif +} + + +static int +am_subscribe_to_ports(void) +{ + if ((port_out = snd_seq_connect_to(seq, port_out, port_nr, subport_nr)) < 0) { + fprintf(stderr, "[SFX] Could not connect to ALSA sequencer port: %s\n", snd_strerror(port_out)); + return SFX_ERROR; + } + return SFX_OK; +} + + +static int +aminit(midi_writer_t *self) +{ + int err; + + snd_midi_event_new(4096, &parser); + snd_midi_event_init(parser); + + sciprintf("[SFX] Initialising ALSA MIDI backend, v%s\n", SCI_ALSA_MIDI_VERSION); + + if (snd_seq_open(&seq, seq_name, SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK)) { + fprintf(stderr, "[SFX] Failed to open ALSA MIDI sequencer '%s' for output\n", + seq_name); + return SFX_ERROR; + } + + if ((port_out = snd_seq_create_simple_port(seq, "FreeSCI", + SND_SEQ_PORT_CAP_WRITE | + SND_SEQ_PORT_CAP_SUBS_WRITE | + SND_SEQ_PORT_CAP_READ, + SND_SEQ_PORT_TYPE_MIDI_GENERIC)) < 0) { + fprintf(stderr, "[SFX] Could not create ALSA sequencer port\n"); + return SFX_ERROR; + } + + if (am_subscribe_to_ports()) + return SFX_ERROR; + + queue = snd_seq_alloc_queue(seq); + _set_tempo(); + + snd_seq_start_queue(seq, queue, NULL); + + if ((err = snd_seq_drain_output(seq))) { + fflush(NULL); + fprintf(stderr, "[SFX] Error while draining: %s\n", + snd_strerror(err)); + return SFX_ERROR; + } + + return SFX_OK; +} + +static int +amsetopt(midi_writer_t *self, char *name, char *value) +{ + return SFX_ERROR; +} + + +static int +amwrite(midi_writer_t *self, unsigned char *buf, int len) +{ + snd_seq_event_t evt; + +#if 0 + { + int i; + fprintf(stderr, "[MID] "); + for (i = 0; i < len; i++) + fprintf(stderr, " %02x", buf[i]); + fprintf(stderr, "\n"); + } +#endif + + snd_seq_ev_clear(&evt); + snd_seq_ev_set_source(&evt, port_out); + snd_seq_ev_set_subs(&evt); /* Broadcast to all subscribers */ + + snd_midi_event_encode(parser, buf, len, &evt); + snd_seq_ev_schedule_tick(&evt, queue, 0, delta); + + snd_seq_event_output_direct(seq, &evt); + +#if 0 + { + snd_seq_queue_status_t *status; + snd_seq_queue_status_malloc(&status); + + snd_seq_get_queue_status(seq, queue, status); + //snd_seq_tick_time_t snd_seq_queue_status_get_tick_time(const snd_seq_queue_status_t *info); + fprintf(stderr, "Queue at %d/%d\n", delta, snd_seq_queue_status_get_tick_time(status)); + + snd_seq_queue_status_free(status); + } +#endif + + + return SFX_OK; +} + +static void +amdelay(midi_writer_t *self, int ticks) +{ + delta += ticks; +} + +static void +amreset_timer(midi_writer_t *self) +{ + snd_seq_drain_output(seq); + snd_seq_stop_queue(seq, queue, NULL); + + + { + snd_seq_event_t evt; + snd_seq_ev_clear(&evt); + snd_seq_ev_set_source(&evt, port_out); + snd_seq_ev_set_subs(&evt); /* Broadcast to all subscribers */ + + snd_seq_ev_set_queue_pos_tick(&evt, queue, 0); + + snd_seq_event_output_direct(seq, &evt); + } + delta = 0; + + + + snd_seq_start_queue(seq, queue, NULL); +} + +static void +amclose(midi_writer_t *self) +{ + snd_midi_event_free(parser); + parser = NULL; +} + + +midi_writer_t sfx_device_midi_alsa = { + "alsa", + aminit, + amsetopt, + amwrite, + amdelay, + NULL, + amreset_timer, + amclose, +}; + +#endif diff --git a/engines/sci/sfx/device/camd-midi.c b/engines/sci/sfx/device/camd-midi.c new file mode 100644 index 0000000000..3af12f196e --- /dev/null +++ b/engines/sci/sfx/device/camd-midi.c @@ -0,0 +1,161 @@ +/*************************************************************************** + camd-midi.c Copyright (C) 2005--08 Walter van Niftrik, Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#ifdef HAVE_PROTO_CAMD_H + +#include "sfx_engine.h" +#include "../device.h" + +#include +#include +#include +#include +#include + + +#define SWAP_BYTES +#define FILL_BYTES + +#define SCI_CAMD_MIDI_VERSION "0.1" +#define SYSEX_PREFIX 0xf0 + +static const char *devicename = "via686.out.0"; + +struct Library *CamdBase = NULL; +struct CamdIFace *ICamd = NULL; +static struct MidiLink *midi_link = NULL; +static struct MidiNode *midi_node = NULL; + +#define ABORT(m) { \ + if (CamdBase) \ + IExec->CloseLibrary(CamdBase); \ + sciprintf("[SFX] CAMD driver: "); \ + sciprintf(m); \ + sciprintf("\n"); \ + return SFX_ERROR; \ + } + +static int +camd_init(midi_writer_t *self) +{ + sciprintf("[SFX] Initialising CAMD raw MIDI backend, v%s\n", SCI_CAMD_MIDI_VERSION); + + CamdBase = IExec->OpenLibrary("camd.library", 36L); + if (!CamdBase) + ABORT("Could not open 'camd.library'"); + + ICamd = (struct CamdIFace *) IExec->GetInterface(CamdBase, "main", 1, NULL); + if (!ICamd) + ABORT("Error while retrieving CAMD interface\n"); + + midi_node = ICamd->CreateMidi(MIDI_MsgQueue, 0L, MIDI_SysExSize, 4096L, MIDI_Name, "freesci", TAG_END); + if (!midi_node) + ABORT("Could not create CAMD MIDI node"); + + midi_link = ICamd->AddMidiLink(midi_node, MLTYPE_Sender, MLINK_Location, devicename, TAG_END); + if (!midi_link) + ABORT(("Could not create CAMD MIDI link to '%s'", devicename)); + + sciprintf("[SFX] CAMD initialisation completed\n"); + + return SFX_OK; +} + +static int +camd_set_option(midi_writer_t *self, char *name, char *value) +{ + return SFX_ERROR; +} + +#define MAX_MIDI_LEN 3 + +static int +camd_write(midi_writer_t *self, unsigned char *buffer, int len) +{ + if (len == 0) + return SFX_OK; + + if (buffer[0] == SYSEX_PREFIX) { + /* Must send this as a SysEx */ + ICamd->PutSysEx(midi_link, buffer); + } else { + ULONG data = 0l; + int i; + int readlen = (len > MAX_MIDI_LEN) ? MAX_MIDI_LEN : len; + + for (i = 0; i < readlen; i++) + if (len >= i) { + data <<= 8; + data |= buffer[i]; + } + data <<= (8 * (sizeof(ULONG) - readlen)); + + if (len > MAX_MIDI_LEN) + sciprintf("[SFX] Warning: Truncated MIDI message to fit CAMD format (sent %d: %02x %02x %02x, real length %d)\n", + MAX_MIDI_LEN, buffer[0], buffer[1], buffer[2], len); + + ICamd->PutMidi(midi_link, data); + } + + return SFX_OK; +} + +static void +camd_delay(midi_writer_t *self, int ticks) +{ +} + +static void +camd_reset_timer(midi_writer_t *self) +{ +} + +static void +camd_close(midi_writer_t *self) +{ +#ifdef NO_OP + return; +#endif + if (CamdBase) + IExec->CloseLibrary(CamdBase); +} + +midi_writer_t sfx_device_midi_camd = { + "camd-midi", + &camd_init, + &camd_set_option, + &camd_write, + &camd_delay, + NULL, + &camd_reset_timer, + &camd_close +}; + +#endif /* HAVE_PROTO_CAMD_H */ diff --git a/engines/sci/sfx/device/devices.c b/engines/sci/sfx/device/devices.c new file mode 100644 index 0000000000..78d8c4279c --- /dev/null +++ b/engines/sci/sfx/device/devices.c @@ -0,0 +1,112 @@ +/*************************************************************************** + devices.c Copyright (C) 2002 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "../device.h" +#include + +#ifndef SCUMMVM +#ifdef HAVE_ALSA +extern struct _midi_device sfx_device_midi_alsa; +#endif +#if !defined(_DOS) && !defined(_WIN32) && !defined(_DREAMCAST) && !defined(__MORPHOS__) && !defined(ARM_WINCE) && !defined(_GP32) +extern struct _midi_device sfx_device_midi_unixraw; +#endif + +#ifdef HAVE_PROTO_CAMD_H +extern struct _midi_device sfx_device_midi_camd; +#endif +#endif // SCUMMVM + +#include + +static struct _midi_device *devices_midi[] = { +#ifndef SCUMMVM +#ifdef HAVE_PROTO_CAMD_H + &sfx_device_midi_camd, +#endif +#ifdef HAVE_ALSA + &sfx_device_midi_alsa, +#endif +#if !defined(_DOS) && !defined(_WIN32) && !defined(_DREAMCAST) && !defined(__MORPHOS__) && !defined(ARM_WINCE) && !defined(_GP32) + &sfx_device_midi_unixraw, +#endif +#endif // SCUMMVM + NULL +}; + +static struct _midi_device *devices_opl2[] = { + NULL +}; + + +/** -- **/ + +struct _midi_device **devices[] = { + NULL, /* No device */ + devices_midi, + devices_opl2, +}; + + +static struct _midi_device * +find_dev(int type, char *name) +{ + int i = 0; + + if (!type) + return NULL; + + if (!name) + return devices[type][0]; + + while (devices[type][i] && !strcmp(name, devices[type][i]->name)) + ++i; + + return devices[type][i]; +} + + +void * +sfx_find_device(int type, char *name) +{ + struct _midi_device *dev = find_dev(type, name); + + if (dev) { + if (dev->init(dev)) { + fprintf(stderr, "[SFX] Opening device '%s' failed\n", + dev->name); + return NULL; + } + + return dev; + }; + + return NULL; +} diff --git a/engines/sci/sfx/device/unixraw-midi.c b/engines/sci/sfx/device/unixraw-midi.c new file mode 100644 index 0000000000..69ce3890fc --- /dev/null +++ b/engines/sci/sfx/device/unixraw-midi.c @@ -0,0 +1,100 @@ +/*************************************************************************** + unixraw-midi.c Copyright (C) 2005 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +#include +#include "../device.h" + +#include +#include +#include + +#define SCI_UNIXRAW_MIDI_VERSION "0.1" + +#ifndef O_SYNC +# define O_SYNC 0 +#endif + +static int fd; +static const char *devicename = "/dev/midi"; + +static int +unixraw_init(midi_writer_t *self) +{ + sciprintf("[SFX] Initialising UNIX raw MIDI backend, v%s\n", SCI_UNIXRAW_MIDI_VERSION); + + fd = open(devicename, O_WRONLY|O_SYNC); + + if (!IS_VALID_FD(fd)) { + sciprintf("[SFX] Failed to open %s\n", devicename); + return SFX_ERROR; + } + + return SFX_OK; +} + +static int +unixraw_set_option(midi_writer_t *self, char *name, char *value) +{ + return SFX_ERROR; +} + +static int +unixraw_write(midi_writer_t *self, unsigned char *buffer, int len) +{ + if (write(fd, buffer, len) != len) { + sciprintf("[SFX] MIDI write error\n"); + return SFX_ERROR; + } + return SFX_OK; +} + +static void +unixraw_delay(midi_writer_t *self, int ticks) +{ +} + +static void +unixraw_reset_timer(midi_writer_t *self) +{ +} + +static void +unixraw_close(midi_writer_t *self) +{ + close(fd); +} + +midi_writer_t sfx_device_midi_unixraw = { + "unixraw-midi", + &unixraw_init, + &unixraw_set_option, + &unixraw_write, + &unixraw_delay, + NULL, + &unixraw_reset_timer, + &unixraw_close +}; diff --git a/engines/sci/sfx/doc/README b/engines/sci/sfx/doc/README new file mode 100644 index 0000000000..502ad7d619 --- /dev/null +++ b/engines/sci/sfx/doc/README @@ -0,0 +1,7 @@ +The files in this direcory are a bit outdated. There are some small non +critical errors in "patch.001", they are obvious if you bother to read +the source (which is correct). "sound01.txt" is just meant to give you a +basic understanding of the structures invovled in +SCI01/SCI1/SCI1.1/SCI32 sound resources. + +Ravi has made some really good documentation for SCI0. diff --git a/engines/sci/sfx/doc/patch001.txt b/engines/sci/sfx/doc/patch001.txt new file mode 100644 index 0000000000..0037eea9b4 --- /dev/null +++ b/engines/sci/sfx/doc/patch001.txt @@ -0,0 +1,118 @@ +SCI patch.001 Resource Format Revision 0.1 +Rickard Lind 1999-12-16 + +The patch.001 file for Roland MT-32, MT-100, LAPC-1, CM-32L and CM-64. + +This specification will sometimes look very incomprehensible without some +knowledge concerning the MT-32 MIDI implementation. +Have a look at http://members.xoom.com/_XMCM/TomLewandowski/docs.html + + +1. The header (494 bytes) which is always present. + +Offset Size Type Description +---------------------------------------------------------------------------- + 0x000 2 89 00 First Two Bytes + 0x002 20 ASCII String for MT-32 Display ("*It's Only A Model* ") + 0x016 20 ASCII String for MT-32 Display (" CAMELOT, CAMELOT! ") + 0x02a 20 ASCII String for MT-32 Display ("Ham & Jam & SpamAlot") + 0x03e 2 word MT-32 Master Volume + 0x040 1 Index in Predefined reverb settings at 0x04c (0-10) + 0x041 11 MT-32 SysEx block setting reverb + (last 3 bytes are dummies) + 0x04c 3 Predefined reverb setting #1 (Mode, Time, Level) + : : : + 0x06a 3 Predefined reverb setting #11 + 0x06d 8 MT-32 Patch Memory #1 (see Patch Memory description below) + : : : + 0x1e5 8 MT-32 Patch Memory #48 + 0x1ed 1 n = Number of Timbre Memory (0-64 userdefined instruments) +---------------------------------------------------------------------------- + + + Patch Memory description + + Offset Description + -------------------------------------------------------------------- + 0x00 Timbre Group (0 = Bank A, 1 = Bank B, 2 = Memory, 3 = Rythm) + 0x01 Timbre Number (0 - 63) + 0x02 Key Shift (0-48) [-24 - +24] + 0x03 Fine Tune (0-100) [-50 - +50] + 0x04 Bender Range (0-24) + 0x05 Assign Mode (0 = Poly 1, 1 = Poly 2, 2 = Poly 3, 3 = Poly 4) + 0x06 Reverb Switch (0 = OFF, 1 = ON) + 0x07 Dummy + -------------------------------------------------------------------- + + Mapping MT-32 to GM instruments is done with Timbre Group and + Timbre Number. + + Instrument 0-63: Bank A, 0-63 + Instrument 64-127: Bank B, 0-63 + + +2. The Timbre Memory block (if n > 0), offset relative to 0x1ee + +Offset Size Type Description +------------------------------------------------------------------------------ + 0x000 246 MT-32 Timbre Memory #1 (see Timbre Memory description below) + : : : + 0x??? 246 MT-32 Timbre Memory #n +------------------------------------------------------------------------------ + + + Timbre Memory description + + Offset Size Description + ----------------------------------------------------------------------- + 0x00 10 Timbre Name (ASCII String) + 0x0a See http://members.xoom.com/_XMCM/TomLewandowski/lapc1.txt + ----------------------------------------------------------------------- + + +3. Second MT-32 Patch Memory Block, offset realtive to 0x1ee + n * 246 + +Offset Size Description +--------------------------------------------------- + 0x000 2 0xab 0xcd (if this this is not present + there is no second block) + 0x002 8 MT-32 Patch Memory #49 + : : : + 0x17a 8 MT-32 Patch Memory #96 +--------------------------------------------------- + + +4. Block for setting up Patch Temporary Area (rythm part) and + System Area - Partial Reserve, offset relative to 0x370 + n * 246 + +Offset Size Description +--------------------------------------------------- + 0x000 2 0xdc 0xba (if this this is not present + this block is non existent) + 0x002 4 Rythm Setup for Key #24 (see below) + : : : + 0x0fe 4 Rythm Setup for Key #87 + 0x102 9 System Area - Partial Reserve +--------------------------------------------------- + + Rythm Setup description + See http://members.xoom.com/_XMCM/TomLewandowski/lapc1.txt + + +TODO: + + * Clearly describe which parts are interesting for a quick and dirty + GeneralMidi/patch.001/FreeSCI implementation + + * Describe how the Sierra MT-32 driver uses patch.001 + + * Make this readable to someone who has not been reading reference + manuals since early childhood + + * SGML + + +Revision history + + Revision 0.1 - 1999-12-16 + - First pre-release of the specification diff --git a/engines/sci/sfx/doc/sound01.txt b/engines/sci/sfx/doc/sound01.txt new file mode 100644 index 0000000000..8aae367da7 --- /dev/null +++ b/engines/sci/sfx/doc/sound01.txt @@ -0,0 +1,213 @@ +The SCI01+ sound resource format + +Originally written by Rickard Lind, 2000-01-05 +Extensively rewritten by Lars Skovlund, 2002-10-27 +Again updated by Lars Skovlund, 2005-10-12 + +Used in: +Quest for Glory II: Trial by Fire (QfG2) +Christmas greeting card 1990 (CC1990) + +The magic number (84 00) is left out, offset 0 is directly after these two +bytes. + +If you examine a SCI01 sound resource use "sciunpack --without-header" to +get the pointers within the file correct for your hex viewer. + +DESCRIPTION +----------- + +The SCI01 sound resource consists of a number of track lists and the +tracks themselves. There is one track list for (almost) every piece of +sound hardware supported by the game. Each track either contains track +data for one specific channel or a digital sample. + +SCI1 resources are the same, except that sample chunks are no longer +allowed (since they are now separate resources). + + Optional Priority Header + ------------------------ + + Some SCI1 songs contain an 8-byte header before the track list. At + least on PC platforms, its data is mostly unused. The priority value + is used if the script does not override it. + + offset size description + ------------------------------------------------------- + 0 byte 0xf0 Priority header marker byte + 1 byte Recommended priority for this song + 2 6 bytes Apparently unused + + Track List + ---------- + + The track list tells us which tracks are to be played on particular + hardware platforms. Each entry either terminates the previous list + or contains an entry for the current one. + + List Termination + ---------------- + + offset size description + ----------------------- + 0 byte 0xff + 1 byte Hardware ID of next list, 0xff if none + + List Entry + ---------- + + offset size description + ----------------------- + 0 byte 0 + 1 byte 0 + 2 word Data Chunk pointer + 4 word Data Chunk size + + The very first list in a file looks a little odd, in that it + starts with a single byte which tells us the hardware ID + associated with the first list (0 in all the cases I've seen) + followed by list entries as usual. + + Known Hardware IDs + ------------------ + + Some of these are used by multiple drivers, probably because they + support the same number of polyphonic voices. Note that the + hardware ID does not necessarily tell us whether samples are + supported - thus, the list for Roland MT-32 also contains sample + tracks, because the user may also have a Sound Blaster card + connected. SCI1 most likely has more hardware IDs than these. + + 0x00 - Sound Blaster, Adlib + 0x06 - MT-32 with Sound Blaster (for digital audio) + 0x09 - CMS/Game Blaster + 0x0c - Roland MT-32 + 0x12 - PC Speaker + 0x13 - IBM PS/1, Tandy 3-voice + + Data Chunks + ----------- + + In the sound resources of QfG2 and CC1990 I've seen two types of Data + Chunks, Sample and MIDI channel track. + + + Sample Chunk + ------------ + + offset size description + ----------------------- + 0 byte =0xfe + 1 byte !=0xfe (always 0 in QfG2 and CC1990) + 2 word Sample rate (Hz) + 4 word Sample length + 6 word Sample point 1 (begin?) + 8 word Sample point 2 (end?) + 10 Unsigned 8-bit mono sample data + + + MIDI channel track Chunk + ------------------------ + + This chunk begins with a 2 byte header. The low nibble of the + first byte indicates the channel number. The high nibble controls + certain aspects of (dynamic) track channel/hardware channel mapping. + + The second byte tells us how many notes will be + playing simultaneously in the channel. From the third byte onward + is the MIDI track data which looks just like normal SCI0 MIDI + track data, but all status bytes are targeted at one specific MIDI + channel. + +Example, sound.833 from QfG2 (--without-header) +----------------------------------------------- + +offset data description +------------------------ + 0000 00 Hardware ID for first track list + 0001 00 Track list continuation + 0002 00 Same hardware device + 0003 003F Data Chunk pointer (Little Endian) + 0005 0013 Data Chunk length (LE) + 0007 00 Track list continuation + 0008 00 Same hardware device + 0009 006A Data Chunk pointer (LE) + 000B 0015 Data Chunk length (LE) + 000D FF Next track list + 000E 09 for hardware device 0x09 + 000F 00 Track list continuation + 0010 00 Same hardware device + 0011 003F Data Chunk pointer (LE) + 0013 0013 Data Chunk length (LE) + 0015 00 Track list continuation + 0016 00 Same hardware device + 0017 0052 Data Chunk pointer (LE) + 0019 0018 Data Chunk length (LE) + 001B 00 Track list continuation + 001C 00 Same hardware device + 001D 0094 Data Chunk pointer (LE) + 001F 0012 Data Chunk length (LE) + 0021 FF Next track list + 0022 0C for hardware device 0x0C + 0023 00 Track list continuation + 0024 00 Same hardware device + 0025 003F Data Chunk pointer (LE) + 0027 0013 Data Chunk length (LE) + 0029 00 Track list continuation + 002A 00 Same hardware device + 002B 0052 Data Chunk pointer (LE) + 002D 0018 Data Chunk length (LE) + 002F FF Next track list + 0030 13 for hardware device 0x13 + 0031 00 Track list continuation + 0032 00 Same hardware device + 0033 003F Data Chunk pointer (LE) + 0035 0013 Data Chunk length (LE) + 0037 00 Track list continuation + 0038 00 Same hardware device + 0039 007F Data Chunk pointer (LE) + 003B 0015 Data Chunk length (LE) + + 003D FF FF Sequence Control - End of Sequence Blocks + ------------------------------------------------------------ + 003F 0F MIDI Track channel 15 (control channel) + 0040 01 One note playing on track (probably just to satisfy the + MIDI engine) + 0041 MIDI Track data like SCI0 + 0052 02 MIDI Track channel 2 + 0053 02 Two notes playing on track + 0054 MIDI Track data like SCI0 + 006A 03 MIDI Track channel 3 + 006B 01 One note playing on track + 006C MIDI Track data like SCI0 + 007F 0A MIDI Track channel 10 + 0080 01 One note playing on track + 0081 MIDI Track data like SCI0 + 0094 02 MIDI Track channel 2 + 0095 01 One note playing on track + 0096 MIDI Track data like SCI0 + +Addendum (provided by Lars Skovlund) +------------------------------------ + +First of all, tracks do not loop individually. No loop signals are +reported. + +Absolute cues are generally stored in the signal selector, and +cumulative cues are generally stored in the dataInc selector, with +some interesting twists: + +1. The server's record of the absolute cue value is reset as part of + UPDATE_CUES. +2. When a cumulative cue is reported to the VM object, it will be + placed in _both_ fields. In such a case, a constant of 0x7f will be + added to the _signal_ selector only, to be able to distinguish the + two kinds of cue (this has already been coded). +3. The above only happens if the sound does not use absolute cues + (i.e. if the signal is 0 a priori). Note that, because of 1) + above, this does not cause problems neither with successive + cumulative cues nor with mixed cumulative/absolute cues. +4. A signal of 0xff will stop the sound object playing. This may be + for purely internal purposes. +5. There no longer is a field indicating the amount of increment for + a cue. diff --git a/engines/sci/sfx/iterator.c b/engines/sci/sfx/iterator.c new file mode 100644 index 0000000000..f6c123a082 --- /dev/null +++ b/engines/sci/sfx/iterator.c @@ -0,0 +1,2113 @@ +/*************************************************************************** + iterator.c Copyright (C) 2001..04 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Song iterators */ + +#include +#include +#include +#include +#include + +static const int MIDI_cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 1, 1, 2, 0}; + +/*#define DEBUG_DECODING*/ +/*#define DEBUG_VERBOSE*/ + +void +print_tabs_id(int nr, songit_id_t id) +{ + while (nr-- > 0) + fprintf(stderr, "\t"); + + fprintf(stderr, "[%08lx] ", id); +} + +#ifndef HAVE_MEMCHR +static void * +memchr(void *_data, int c, int n) +{ + unsigned char *data = (unsigned char *) _data; + + while (n && !(*data == c)) { + ++data; + --n; + } + + if (n) + return data; + else + return NULL; +} +#endif + +static void +_common_init(base_song_iterator_t *self) +{ + self->fade.action = FADE_ACTION_NONE; + self->resetflag = 0; + self->loops = 0; + self->priority = 0; +} + + +/************************************/ +/*-- SCI0 iterator implementation --*/ +/************************************/ + +#define SCI0_MIDI_OFFSET 33 +#define SCI0_END_OF_SONG 0xfc /* proprietary MIDI command */ + +#define SCI0_PCM_SAMPLE_RATE_OFFSET 0x0e +#define SCI0_PCM_SIZE_OFFSET 0x20 +#define SCI0_PCM_DATA_OFFSET 0x2c + +#define CHECK_FOR_END_ABSOLUTE(offset) \ + if (offset > self->size) { \ + fprintf(stderr, SIPFX "Reached end of song without terminator (%x/%x) at %d!\n", offset, self->size, __LINE__); \ + return SI_FINISHED; \ + } + +#define CHECK_FOR_END(offset_augment) \ + if ((channel->offset + (offset_augment)) > channel->end) { \ + channel->state = SI_STATE_FINISHED; \ + fprintf(stderr, SIPFX "Reached end of track %d without terminator (%x+%x/%x) at %d!\n", channel->id, channel->offset, offset_augment, channel->end, __LINE__); \ + return SI_FINISHED; \ + } + + +static inline int +_parse_ticks(byte *data, int *offset_p, int size) +{ + int ticks = 0; + int tempticks; + int offset = 0; + + do { + tempticks = data[offset++]; + ticks += (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX)? + SCI_MIDI_TIME_EXPANSION_LENGTH : tempticks; + } while (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX + && offset < size); + + if (offset_p) + *offset_p = offset; + + return ticks; +} + + +static int +_sci0_read_next_command(sci0_song_iterator_t *self, + unsigned char *buf, int *result); + + +static int +_sci0_get_pcm_data(sci0_song_iterator_t *self, + sfx_pcm_config_t *format, + int *xoffset, + unsigned int *xsize); + +#define PARSE_FLAG_LOOPS_UNLIMITED (1 << 0) /* Unlimited # of loops? */ +#define PARSE_FLAG_PARAMETRIC_CUE (1 << 1) /* Assume that cues take an additional "cue value" argument */ +/* This implements a difference between SCI0 and SCI1 cues. */ + +void +_reset_synth_channels(base_song_iterator_t *self, song_iterator_channel_t *channel) +{ + int i; + byte buf[5]; + tell_synth_func *tell = sfx_get_player_tell_func(); + + for (i = 0; i < MIDI_CHANNELS; i++) + { + if (channel->saw_notes & (1 << i)) + { + buf[0] = 0xe0 | i; /* Pitch bend */ + buf[1] = 0x80; /* Wheel center */ + buf[2] = 0x40; + if (tell) + tell(3, buf); + /* TODO: Reset other controls? */ + } + } +} + +static int +_parse_sci_midi_command(base_song_iterator_t *self, unsigned char *buf, int *result, + song_iterator_channel_t *channel, + int flags) +{ + unsigned char cmd; + int paramsleft; + int midi_op; + int midi_channel; + + channel->state = SI_STATE_DELTA_TIME; + + cmd = self->data[channel->offset++]; + + if (!(cmd & 0x80)) { + /* 'Running status' mode */ + channel->offset--; + cmd = channel->last_cmd; + } + + if (cmd == 0xfe) + { + fprintf(stderr, "song iterator subsystem: Corrupted sound resource detected.\n"); + return SI_FINISHED; + } + + midi_op = cmd >> 4; + midi_channel = cmd & 0xf; + paramsleft = MIDI_cmdlen[midi_op]; + channel->saw_notes |= 1 << midi_channel; + +#if 0 +if (1) { + fprintf(stderr, "[IT]: off=%x, cmd=%02x, takes %d args ", + channel->offset - 1, cmd, paramsleft); + fprintf(stderr, "[%02x %02x <%02x> %02x %02x %02x]\n", + self->data[channel->offset-3], + self->data[channel->offset-2], + self->data[channel->offset-1], + self->data[channel->offset], + self->data[channel->offset+1], + self->data[channel->offset+2]); +} +#endif + + buf[0] = cmd; + + + CHECK_FOR_END(paramsleft); + memcpy(buf + 1, self->data + channel->offset, paramsleft); + *result = 1 + paramsleft; + + channel->offset += paramsleft; + + channel->last_cmd = cmd; + + /* Are we supposed to play this channel? */ + if ( + /* First, exclude "global" properties-- such as cues-- from consideration */ + (midi_op < 0xf + && !(cmd == SCI_MIDI_SET_SIGNAL) + && !(SCI_MIDI_CONTROLLER(cmd) + && buf[1] == SCI_MIDI_CUMULATIVE_CUE)) + + /* Next, check if the channel is allowed */ + && (!((1 << midi_channel) & channel->playmask))) + return /* Execute next command */ + self->next((song_iterator_t *) self, buf, result); + + + if (cmd == SCI_MIDI_EOT) { + /* End of track? */ + _reset_synth_channels(self, channel); +/* fprintf(stderr, "eot; loops = %d, notesplayed=%d\n", self->loops, channel->notes_played);*/ + if (self->loops > 1 /* && channel->notes_played*/) { + /* If allowed, decrement the number of loops */ + if (!(flags & PARSE_FLAG_LOOPS_UNLIMITED)) + *result = --self->loops; + +#ifdef DEBUG_DECODING + fprintf(stderr, "%s L%d: (%p):%d Looping ", __FILE__, __LINE__, self, channel->id); + if (flags & PARSE_FLAG_LOOPS_UNLIMITED) + fprintf(stderr, "(indef.)"); + else + fprintf(stderr, "(%d)", self->loops); + fprintf(stderr, " %x -> %x\n", + channel->offset, channel->loop_offset); +#endif + channel->offset = channel->loop_offset; + channel->notes_played = 0; + channel->state = SI_STATE_DELTA_TIME; + channel->total_timepos = channel->loop_timepos; + channel->last_cmd = 0xfe; + fprintf(stderr, "Looping song iterator %08lx.\n", self->ID); + return SI_LOOP; + } else { + channel->state = SI_STATE_FINISHED; +#ifdef DEBUG_DECODING + fprintf(stderr, "%s L%d: (%p):%d EOT because" + " %d notes, %d loops\n", + __FILE__, __LINE__, self, channel->id, + channel->notes_played, self->loops); +#endif + return SI_FINISHED; + } + + } else if (cmd == SCI_MIDI_SET_SIGNAL) { + if (buf[1] == SCI_MIDI_SET_SIGNAL_LOOP) { + channel->loop_offset = channel->offset; + channel->loop_timepos = channel->total_timepos; + + return /* Execute next command */ + self->next((song_iterator_t *) self, buf, result); + } else { + /* Used to be conditional <= 127 */ + *result = buf[1]; /* Absolute cue */ + return SI_ABSOLUTE_CUE; + } + } else if (SCI_MIDI_CONTROLLER(cmd)) { + switch (buf[1]) { + + case SCI_MIDI_CUMULATIVE_CUE: + if (flags & PARSE_FLAG_PARAMETRIC_CUE) + self->ccc += buf[2]; + else { /* No parameter to CC */ + self->ccc++; +/* channel->offset--; */ + } + *result = self->ccc; + return SI_RELATIVE_CUE; + + case SCI_MIDI_RESET_ON_SUSPEND: + self->resetflag = buf[2]; + break; + + case SCI_MIDI_SET_POLYPHONY: + self->polyphony[midi_channel] = buf[2]; + +#if 0 + { + int i; + int voices = 0; + for (i = 0; i < ((sci1_song_iterator_t *) self)->channels_nr; i++) + { + voices += self->polyphony[i]; + } + + sciprintf("SET_POLYPHONY(%d, %d) for a total of %d voices\n", midi_channel, buf[2], voices); + sciprintf("[iterator-1] DEBUG: Polyphony = [ "); + for (i = 0; i < ((sci1_song_iterator_t *) self)->channels_nr; i++) + sciprintf("%d ", self->polyphony[i]); + sciprintf("]\n"); + sciprintf("[iterator-1] DEBUG: Importance = [ "); + for (i = 0; i < ((sci1_song_iterator_t *) self)->channels_nr; i++) + sciprintf("%d ", self->importance[i]); + sciprintf("]\n"); + } +#endif + break; + + case SCI_MIDI_SET_REVERB: + break; + + case SCI_MIDI_CHANNEL_MUTE: + sciprintf("CHANNEL_MUTE(%d, %d)\n", midi_channel, buf[2]); + break; + + case SCI_MIDI_HOLD: + { + // Safe cast: This controller is only used in SCI1 + sci1_song_iterator_t *self1 = (sci1_song_iterator_t *) self; + + if (buf[2] == self1->hold) + { + channel->offset = channel->initial_offset; + channel->notes_played = 0; + channel->state = SI_STATE_COMMAND; + channel->total_timepos = 0; + + self1->channels_looped = self1->active_channels-1; + + return SI_LOOP; + } + + break; + } + case 0x04: /* UNKNOWN NYI (happens in LSL2 gameshow) */ + case 0x46: /* UNKNOWN NYI (happens in LSL3 binoculars) */ + case 0x61: /* UNKNOWN NYI (special for adlib? Iceman) */ + case 0x73: /* UNKNOWN NYI (happens in Hoyle) */ + case 0xd1: /* UNKNOWN NYI (happens in KQ4 when riding the unicorn) */ + return /* Execute next command */ + self->next((song_iterator_t *) self, buf, result); + + case 0x01: /* modulation */ + case 0x07: /* volume */ + case 0x0a: /* panpot */ + case 0x0b: /* expression */ + case 0x40: /* hold */ + case 0x79: /* reset all */ + /* No special treatment neccessary */ + break; + + } + return 0; + + } else { + if ((cmd & 0xf0) == 0x90) /* note on? */ + channel->notes_played++; + + /* Process as normal MIDI operation */ + return 0; + } +} + + +static int +_sci_midi_process_state(base_song_iterator_t *self, unsigned char *buf, int *result, + song_iterator_channel_t *channel, + int flags) +{ + CHECK_FOR_END(0); + + switch (channel->state) { + + case SI_STATE_PCM: { + if (*(self->data + channel->offset) == 0 + && *(self->data + channel->offset + 1) == SCI_MIDI_EOT) + /* Fake one extra tick to trick the interpreter into not killing the song iterator right away */ + channel->state = SI_STATE_PCM_MAGIC_DELTA; + else + channel->state = SI_STATE_DELTA_TIME; + return SI_PCM; + } + + case SI_STATE_PCM_MAGIC_DELTA: { + sfx_pcm_config_t format; + int offset; + unsigned int size; + int delay; + if (_sci0_get_pcm_data((sci0_song_iterator_t *) self, &format, &offset, &size)) + return SI_FINISHED; /* 'tis broken */ + channel->state = SI_STATE_FINISHED; + delay = (size * 50 + format.rate - 1) / format.rate; /* number of ticks to completion*/ + + fprintf(stderr, "delaying %d ticks\n", delay); + return delay; + } + + case SI_STATE_UNINITIALISED: + fprintf(stderr, SIPFX "Attempt to read command from uninitialized iterator!\n"); + self->init((song_iterator_t *) self); + return self->next((song_iterator_t *) self, buf, result); + + case SI_STATE_FINISHED: + return SI_FINISHED; + + case SI_STATE_DELTA_TIME: { + int offset; + int ticks = _parse_ticks(self->data + channel->offset, + &offset, + self->size - channel->offset); + + channel->offset += offset; + channel->delay += ticks; + channel->timepos_increment = ticks; + + CHECK_FOR_END(0); + + channel->state = SI_STATE_COMMAND; + + if (ticks) + return ticks; + } + + /* continute otherwise... */ + + case SI_STATE_COMMAND: { + int retval; + channel->total_timepos += channel->timepos_increment; + channel->timepos_increment = 0; + + retval = _parse_sci_midi_command(self, buf, result, + channel, flags); + + if (retval == SI_FINISHED) { + if (self->active_channels) + --(self->active_channels); +#ifdef DEBUG_DECODING + fprintf(stderr, "%s L%d: (%p):%d Finished channel, %d channels left\n", + __FILE__, __LINE__, self, channel->id, + self->active_channels); +#endif + /* If we still have channels left... */ + if (self->active_channels) { + return self->next((song_iterator_t *) self, buf, result); + } + + /* Otherwise, we have reached the end */ + self->loops = 0; + } + + return retval; + } + + default: + fprintf(stderr, SIPFX "Invalid iterator state %d!\n", + channel->state); + BREAKPOINT(); + return SI_FINISHED; + } +} + +static inline int +_sci_midi_process(base_song_iterator_t *self, unsigned char *buf, int *result, + song_iterator_channel_t *channel, + int flags) +{ + return _sci_midi_process_state(self, buf, result, + channel, + flags); +} + +static int +_sci0_read_next_command(sci0_song_iterator_t *self, unsigned char *buf, + int *result) +{ + return _sci_midi_process((base_song_iterator_t *) self, buf, result, + &(self->channel), + PARSE_FLAG_PARAMETRIC_CUE); +} + + +static inline int +_sci0_header_magic_p(unsigned char *data, int offset, int size) +{ + if (offset + 0x10 > size) + return 0; + return + (data[offset] == 0x1a) + && (data[offset + 1] == 0x00) + && (data[offset + 2] == 0x01) + && (data[offset + 3] == 0x00); +} + + +static int +_sci0_get_pcm_data(sci0_song_iterator_t *self, + sfx_pcm_config_t *format, + int *xoffset, + unsigned int *xsize) +{ + int tries = 2; + int found_it = 0; + unsigned char *pcm_data; + int size; + unsigned int offset = SCI0_MIDI_OFFSET; + + if (self->data[0] != 2) + return 1; + /* No such luck */ + + while ((tries--) && (offset < self->size) && (!found_it)) { + /* Search through the garbage manually */ + unsigned char *fc = (unsigned char*)memchr(self->data + offset, + SCI0_END_OF_SONG, + self->size - offset); + + if (!fc) { + fprintf(stderr, SIPFX "Warning: Playing unterminated" + " song!\n"); + return 1; + } + + /* add one to move it past the END_OF_SONG marker */ + offset = fc - self->data + 1; + + + if (_sci0_header_magic_p(self->data, offset, self->size)) + found_it = 1; + } + + if (!found_it) { + fprintf(stderr, SIPFX + "Warning: Song indicates presence of PCM, but" + " none found (finally at offset %04x)\n", offset); + + return 1; + } + + pcm_data = self->data + offset; + + size = getUInt16(pcm_data + SCI0_PCM_SIZE_OFFSET); + + /* Two of the format parameters are fixed by design: */ + format->format = SFX_PCM_FORMAT_U8; + format->stereo = SFX_PCM_MONO; + format->rate = getUInt16(pcm_data + SCI0_PCM_SAMPLE_RATE_OFFSET); + + if (offset + SCI0_PCM_DATA_OFFSET + size != self->size) { + int d = offset + SCI0_PCM_DATA_OFFSET + size - self->size; + + fprintf(stderr, SIPFX + "Warning: PCM advertizes %d bytes of data, but %d" + " bytes are trailing in the resource!\n", + size, self->size - (offset + SCI0_PCM_DATA_OFFSET)); + + if (d > 0) + size -= d; /* Fix this */ + } + + *xoffset = offset; + *xsize = size; + + return 0; + } + +static sfx_pcm_feed_t * +_sci0_check_pcm(sci0_song_iterator_t *self) +{ + sfx_pcm_config_t format; + int offset; + unsigned int size; + if (_sci0_get_pcm_data(self, &format, &offset, &size)) + return NULL; + + self->channel.state + = SI_STATE_FINISHED; /* Don't play both PCM and music */ + + return sfx_iterator_make_feed(self->data, + offset + SCI0_PCM_DATA_OFFSET, + size, + format); +} + +static song_iterator_t * +_sci0_handle_message(sci0_song_iterator_t *self, song_iterator_message_t msg) +{ + if (msg.recipient == _SIMSG_BASE) { + switch (msg.type) { + + case _SIMSG_BASEMSG_PRINT: + print_tabs_id(msg.args[0].i, self->ID); + fprintf(stderr, "SCI0: dev=%d, active-chan=%d, size=%d, loops=%d\n", + self->device_id, self->active_channels, self->size, + self->loops); + break; + + case _SIMSG_BASEMSG_SET_LOOPS: + self->loops = msg.args[0].i; + break; + + case _SIMSG_BASEMSG_CLONE: { + int tsize = sizeof(sci0_song_iterator_t); + base_song_iterator_t *mem = (base_song_iterator_t*)sci_malloc(tsize); + memcpy(mem, self, tsize); + sci_refcount_incref(mem->data); +#ifdef DEBUG_VERBOSE +fprintf(stderr, "** CLONE INCREF for new %p from %p at %p\n", mem, self, mem->data); +#endif + return (struct _song_iterator *) mem; /* Assume caller has another copy of this */ + } + + case _SIMSG_BASEMSG_STOP: { + songit_id_t sought_id = msg.ID; + + if (sought_id == self->ID) + self->channel.state = SI_STATE_FINISHED; + break; + } + + case _SIMSG_BASEMSG_SET_PLAYMASK: { + int i; + self->device_id = msg.args[0].i; + + /* Set all but the rhytm channel mask bits */ + self->channel.playmask &= ~(1 << MIDI_RHYTHM_CHANNEL); + + for (i = 0; i < MIDI_CHANNELS; i++) + if (self->data[2 + (i << 1)] & self->device_id + && i != MIDI_RHYTHM_CHANNEL) + self->channel.playmask |= (1 << i); + } + break; + + case _SIMSG_BASEMSG_SET_RHYTHM: + self->channel.playmask &= ~(1 << MIDI_RHYTHM_CHANNEL); + if (msg.args[0].i) + self->channel.playmask |= (1 << MIDI_RHYTHM_CHANNEL); + break; + + case _SIMSG_BASEMSG_SET_FADE: + { + fade_params_t *fp = (fade_params_t *) msg.args[0].p; + self->fade.action = fp->action; + self->fade.final_volume = fp->final_volume; + self->fade.ticks_per_step = fp->ticks_per_step; + self->fade.step_size = fp->step_size; + break; + } + + default: + return NULL; + } + + return (song_iterator_t *)self; + } + return NULL; +} + +static int +_sci0_get_timepos(sci0_song_iterator_t *self) +{ + return self->channel.total_timepos; +} + +static void +_base_init_channel(song_iterator_channel_t *channel, int id, int offset, + int end) +{ + channel->playmask = PLAYMASK_NONE; /* Disable all channels */ + channel->id = id; + channel->notes_played = 0; + channel->state = SI_STATE_DELTA_TIME; + channel->loop_timepos = 0; + channel->total_timepos = 0; + channel->timepos_increment = 0; + channel->delay = 0; /* Only used for more than one channel */ + channel->last_cmd = 0xfe; + + channel->offset + = channel->loop_offset + = channel->initial_offset + = offset; + channel->end = end; + channel->saw_notes = 0; +} + +static void +_sci0_init(sci0_song_iterator_t *self) +{ + _common_init((base_song_iterator_t *) self); + + self->ccc = 0; /* Reset cumulative cue counter */ + self->active_channels = 1; + _base_init_channel(&(self->channel), 0, SCI0_MIDI_OFFSET, self->size); + _reset_synth_channels((base_song_iterator_t *) self, + &(self->channel)); + self->delay_remaining = 0; + + if (self->data[0] == 2) /* Do we have an embedded PCM? */ + self->channel.state = SI_STATE_PCM; +} + + +static void +_sci0_cleanup(sci0_song_iterator_t *self) +{ +#ifdef DEBUG_VERBOSE +fprintf(stderr, "** FREEING it %p: data at %p\n", self, self->data); +#endif + if (self->data) + sci_refcount_decref(self->data); + self->data = NULL; +} + +/***************************/ +/*-- SCI1 song iterators --*/ +/***************************/ + +#define SCI01_INVALID_DEVICE 0xff + +/* First index determines whether DSP output is supported */ +static int sci0_to_sci1_device_map[][2] = { + {0x06, 0x0c}, /* MT-32 */ + {0xff, 0xff}, /* YM FB-01 */ + {0x00, 0x00}, /* CMS/Game Blaster-- we assume OPL/2 here... */ + {0xff, 0xff}, /* Casio MT540/CT460 */ + {0x13, 0x13}, /* Tandy 3-voice */ + {0x12, 0x12}, /* PC speaker */ + {0xff, 0xff}, + {0xff, 0xff}, +}; /* Maps bit number to device ID */ + +#define SONGDATA(x) self->data[offset + (x)] +#define SCI1_CHANDATA(off) self->data[channel->offset + (off)] + +static int +_sci1_sample_init(sci1_song_iterator_t *self, int offset) +{ + sci1_sample_t *sample, **seekerp; + int rate; + int length; + int begin; + int end; + + CHECK_FOR_END_ABSOLUTE(offset + 10); + if (self->data[offset + 1] != 0) + sciprintf("[iterator-1] In sample at offset 0x04x: Byte #1 is %02x instead of zero\n", + self->data[offset + 1]); + + rate = getInt16(self->data + offset + 2); + length = getUInt16(self->data + offset + 4); + begin = getInt16(self->data + offset + 6); + end = getInt16(self->data + offset + 8); + + CHECK_FOR_END_ABSOLUTE(offset + 10 + length); + + sample = (sci1_sample_t*)sci_malloc(sizeof(sci1_sample_t)); + sample->delta = begin; + sample->size = length; + sample->data = self->data + offset + 10; + +#ifdef DEBUG_VERBOSE + fprintf(stderr, "[SAMPLE] %x/%x/%x/%x l=%x\n", + offset + 10, begin, end, self->size, length); +#endif + + sample->format.format = SFX_PCM_FORMAT_U8; + sample->format.stereo = SFX_PCM_MONO; + sample->format.rate = rate; + + sample->announced = 0; + + /* Perform insertion sort */ + seekerp = &(self->next_sample); + + while (*seekerp && (*seekerp)->delta < begin) + seekerp = &((*seekerp)->next); + + sample->next = *seekerp; + *seekerp = sample; + + return 0; /* Everything's fine */ +} + + +static int +_sci1_song_init(sci1_song_iterator_t *self) +{ + sci1_sample_t *seeker; + int last_time; + int offset = 0; + self->channels_nr = 0; + self->next_sample = 0; +// self->device_id = 0x0c; + + CHECK_FOR_END_ABSOLUTE(0); + if (SONGDATA(0) == 0xf0) + { + self->priority = SONGDATA(1); + + offset += 8; + } + + while (SONGDATA(0) != 0xff + && SONGDATA(0) != self->device_id) { + offset++; + CHECK_FOR_END_ABSOLUTE(offset + 1); + while (SONGDATA(0) != 0xff) { + CHECK_FOR_END_ABSOLUTE(offset + 7); + offset += 6; + } + offset++; + } + + if (SONGDATA(0) == 0xff) { + sciprintf("[iterator-1] Song does not support" + " hardware 0x%02x\n", + self->device_id); + return 1; + } + + offset++; + + while (SONGDATA(0) != 0xff) { /* End of list? */ + int track_offset; + int end; + offset += 2; + + CHECK_FOR_END_ABSOLUTE(offset + 4); + + track_offset = getUInt16(self->data + offset); + end = getUInt16(self->data + offset + 2); + + CHECK_FOR_END_ABSOLUTE(track_offset - 1); + + if (self->data[track_offset] == 0xfe) { + if (_sci1_sample_init(self, track_offset)) + return 1; /* Error */ + } else { + /* Regular MIDI channel */ + if (self->channels_nr >= MIDI_CHANNELS) { + sciprintf("[iterator-1] Warning: Song has more than %d channels, cutting them off\n", + MIDI_CHANNELS); + break; /* Scan for remaining samples */ + } else { + int channel_nr + = self->data[track_offset] & 0xf; + song_iterator_channel_t *channel = + &(self->channels[self->channels_nr++]); + + if (self->data[track_offset] & 0xf0) + printf("Channel %d has mapping bits %02x\n", + channel_nr, self->data[track_offset] & 0xf0); + + _base_init_channel(channel, + channel_nr, + /* Skip over header bytes: */ + track_offset + 2, + track_offset + end); + _reset_synth_channels((base_song_iterator_t *) self, + channel); + + self->polyphony[self->channels_nr - 1] + = SCI1_CHANDATA(-1); + self->importance[self->channels_nr - 1] + = self->polyphony[self->channels_nr - 1] >> 4; + self->polyphony[self->channels_nr - 1] &= 15; + + channel->playmask = ~0; /* Enable all */ + self->channel_mask |= (1 << channel_nr); + + CHECK_FOR_END_ABSOLUTE(offset + end); + } + } + offset += 4; + CHECK_FOR_END_ABSOLUTE(offset); + } + + /* Now ensure that sapmle deltas are relative to the previous sample */ + seeker = self->next_sample; + last_time = 0; + self->active_channels = self->channels_nr; + self->channels_looped = 0; + + while (seeker) { + int prev_last_time = last_time; + sciprintf("[iterator-1] Detected sample: %d Hz, %d bytes at time %d\n", + seeker->format.rate, seeker->size, seeker->delta); + last_time = seeker->delta; + seeker->delta -= prev_last_time; + seeker = seeker->next; + } + + return 0; /* Success */ +} + +#undef SONGDATA + +static inline int +_sci1_get_smallest_delta(sci1_song_iterator_t *self) +{ + int i, d = -1; + for (i = 0; i < self->channels_nr; i++) + if (self->channels[i].state == SI_STATE_COMMAND + && (d == -1 || self->channels[i].delay < d)) + d = self->channels[i].delay; + + if (self->next_sample && self->next_sample->delta < d) + return self->next_sample->delta; + else + return d; +} + +static inline void +_sci1_update_delta(sci1_song_iterator_t *self, int delta) +{ + int i; + + if (self->next_sample) + self->next_sample->delta -= delta; + + for (i = 0; i < self->channels_nr; i++) + if (self->channels[i].state == SI_STATE_COMMAND) + self->channels[i].delay -= delta; +} + +static inline int +_sci1_no_delta_time(sci1_song_iterator_t *self) +{ /* Checks that none of the channels is waiting for its delta to be read */ + int i; + + for (i = 0; i < self->channels_nr; i++) + if (self->channels[i].state == SI_STATE_DELTA_TIME) + return 0; + + return 1; +} + +static void +_sci1_dump_state(sci1_song_iterator_t *self) +{ + int i; + + sciprintf("-- [%p] ------------------------\n", self); + for (i = 0; i < self->channels_nr; i++) { + int j; + sciprintf("%d(s%02d): d-%d:\t(%x/%x) ", + self->channels[i].id, + self->channels[i].state, + self->channels[i].delay, + self->channels[i].offset, + self->channels[i].end); + for (j = -3; j < 9; j++) { + if (j == 0) + sciprintf(">"); + else + sciprintf(" "); + + sciprintf("%02x", self->data[self->channels[i].offset+j]); + + if (j == 0) + sciprintf("<"); + else + sciprintf(" "); + } + sciprintf("\n"); + } + if (self->next_sample) { + sciprintf("\t[sample %d]\n", + self->next_sample->delta); + } + sciprintf("------------------------------------------\n"); +} + +#define COMMAND_INDEX_NONE -1 +#define COMMAND_INDEX_PCM -2 + +static inline int /* Determine the channel # of the next active event, or -1 */ +_sci1_command_index(sci1_song_iterator_t *self) +{ + int i; + int base_delay = 0x7ffffff; + int best_chan = COMMAND_INDEX_NONE; + + for (i = 0; i < self->channels_nr; i++) + if ((self->channels[i].state != SI_STATE_PENDING) + && (self->channels[i].state != SI_STATE_FINISHED)) { + + if ((self->channels[i].state == SI_STATE_DELTA_TIME) + && (self->channels[i].delay == 0)) + return i; + /* First, read all unknown delta times */ + + if (self->channels[i].delay < base_delay) { + best_chan = i; + base_delay = self->channels[i].delay; + } + } + + if (self->next_sample && base_delay >= self->next_sample->delta) + return COMMAND_INDEX_PCM; + + return best_chan; +} + + +static sfx_pcm_feed_t * +_sci1_get_pcm(sci1_song_iterator_t *self) +{ + if (self->next_sample + && self->next_sample->delta <= 0) { + sci1_sample_t *sample = self->next_sample; + sfx_pcm_feed_t *feed + = sfx_iterator_make_feed(self->data, + sample->data - self->data, + sample->size, + sample->format); + + self->next_sample = self->next_sample->next; + + sci_free(sample); + + return feed; + } else + return NULL; +} + + +static int +_sci1_process_next_command(sci1_song_iterator_t *self, + unsigned char *buf, int *result) +{ + int retval = -42; /* Shouldn't happen, but gcc doesn't agree */ + int chan; + + if (!self->initialised) { + sciprintf("[iterator-1] DEBUG: Initialising for %d\n", + self->device_id); + self->initialised = 1; + if (_sci1_song_init(self)) + return SI_FINISHED; + } + + + if (self->delay_remaining) { + int delay = self->delay_remaining; + self->delay_remaining = 0; + return delay; + } + + do { + chan = _sci1_command_index(self); + + if (chan == COMMAND_INDEX_NONE) { + return SI_FINISHED; + } + + if (chan == COMMAND_INDEX_PCM) { + + if (self->next_sample->announced) { + /* Already announced; let's discard it */ + sfx_pcm_feed_t *feed + = _sci1_get_pcm(self); + feed->destroy(feed); + } else { + int delay = self->next_sample->delta; + + if (delay) { + _sci1_update_delta(self, delay); + return delay; + } + /* otherwise we're touching a PCM */ + self->next_sample->announced = 1; + return SI_PCM; + } + } else { /* Not a PCM */ + + retval = _sci_midi_process((base_song_iterator_t *) self, + buf, result, + &(self->channels[chan]), + PARSE_FLAG_LOOPS_UNLIMITED); + + if (retval == SI_LOOP) { + self->channels_looped++; + self->channels[chan].state = SI_STATE_PENDING; + self->channels[chan].delay = 0; + + if (self->channels_looped == self->active_channels) { + int i; + + /* Everyone's ready: Let's loop */ + for (i = 0; i < self->channels_nr; i++) + if (self->channels[i].state + == SI_STATE_PENDING) + self->channels[i].state + = SI_STATE_DELTA_TIME; + + self->channels_looped = 0; + return SI_LOOP; + } + } else if (retval == SI_FINISHED) { +#ifdef DEBUG + fprintf(stderr, "FINISHED some channel\n"); +#endif + } else if (retval > 0) { + int sd ; + sd = _sci1_get_smallest_delta(self); + + if (_sci1_no_delta_time(self) && sd) { + /* No other channel is ready */ + _sci1_update_delta(self, sd); + + /* Only from here do we return delta times */ + return sd; + } + } + + } /* Not a PCM */ + + } while (retval > 0); /* All delays must be processed separately */ + + return retval; +} + +static struct _song_iterator * +_sci1_handle_message(sci1_song_iterator_t *self, + song_iterator_message_t msg) +{ + if (msg.recipient == _SIMSG_BASE) { /* May extend this in the future */ + switch (msg.type) { + + case _SIMSG_BASEMSG_PRINT: { + int playmask = 0; + int i; + + for (i = 0; i < self->channels_nr; i++) + playmask |= self->channels[i].playmask; + + print_tabs_id(msg.args[0].i, self->ID); + fprintf(stderr, "SCI1: chan-nr=%d, playmask=%04x\n", + self->channels_nr, playmask); + } + break; + + case _SIMSG_BASEMSG_CLONE: { + int tsize = sizeof(sci1_song_iterator_t); + sci1_song_iterator_t *mem = (sci1_song_iterator_t*)sci_malloc(tsize); + sci1_sample_t **samplep; + int delta = msg.args[0].i; /* Delay until next step */ + + memcpy(mem, self, tsize); + samplep = &(mem->next_sample); + + sci_refcount_incref(mem->data); + + mem->delay_remaining += delta; + + /* Clone chain of samples */ + while (*samplep) { + sci1_sample_t *newsample + = (sci1_sample_t*)sci_malloc(sizeof(sci1_sample_t)); + memcpy(newsample, *samplep, + sizeof(sci1_sample_t)); + *samplep = newsample; + samplep = &(newsample->next); + } + + return (struct _song_iterator *) mem; /* Assume caller has another copy of this */ + } + + case _SIMSG_BASEMSG_STOP: { + songit_id_t sought_id = msg.ID; + int i; + + if (sought_id == self->ID) { + self->ID = 0; + + for (i = 0; i < self->channels_nr; i++) + self->channels[i].state = SI_STATE_FINISHED; + } + break; + } + + case _SIMSG_BASEMSG_SET_PLAYMASK: if (msg.ID == self->ID) { + self->channel_mask = 0; + + self->device_id + = sci0_to_sci1_device_map + [sci_ffs(msg.args[0].i & 0xff) - 1] + [sfx_pcm_available()] + ; + + if (self->device_id == 0xff) { + sciprintf("[iterator-1] Warning: Device %d(%d) not supported", + msg.args[0].i & 0xff, sfx_pcm_available()); + } + if (self->initialised) { + int i; + int toffset = -1; + + for (i = 0; i < self->channels_nr; i++) + if (self->channels[i].state != SI_STATE_FINISHED + && self->channels[i].total_timepos > toffset) { + toffset = self->channels[i].total_timepos + + self->channels[i].timepos_increment + - self->channels[i].delay; + } + + /* Find an active channel so that we can + ** get the correct time offset */ + + _sci1_song_init(self); + + toffset -= self->delay_remaining; + self->delay_remaining = 0; + + if (toffset > 0) + return new_fast_forward_iterator((song_iterator_t *) self, + toffset); + } else { + _sci1_song_init(self); + self->initialised = 1; + } + + break; + + } + + case _SIMSG_BASEMSG_SET_LOOPS: + if (msg.ID == self->ID) + self->loops = (msg.args[0].i > 32767)? 99 : 0; + /* 99 is arbitrary, but we can't use '1' because of + ** the way we're testing in the decoding section. */ + break; + + case _SIMSG_BASEMSG_SET_HOLD: + self->hold = msg.args[0].i; + break; + case _SIMSG_BASEMSG_SET_RHYTHM: + /* Ignore */ + break; + + case _SIMSG_BASEMSG_SET_FADE: + { + fade_params_t *fp = (fade_params_t *) msg.args[0].p; + self->fade.action = fp->action; + self->fade.final_volume = fp->final_volume; + self->fade.ticks_per_step = fp->ticks_per_step; + self->fade.step_size = fp->step_size; + break; + } + + default: + fprintf(stderr, SIPFX "Unsupported command %d to" + " SCI1 iterator", msg.type); + } + return (song_iterator_t *) self; + } + return NULL; +} + + +static int +_sci1_read_next_command(sci1_song_iterator_t *self, + unsigned char *buf, int *result) +{ + return _sci1_process_next_command(self, buf, result); +} + + +static void +_sci1_init(sci1_song_iterator_t *self) +{ + _common_init((base_song_iterator_t *) self); + self->ccc = 127; + self->device_id = 0x00; /* Default to Sound Blaster/Adlib for purposes + ** of cue computation */ + self->next_sample = NULL; + self->channels_nr = 0; + self->initialised = 0; + self->delay_remaining = 0; + self->loops = 0; + self->hold = 0; + memset(self->polyphony, 0, sizeof(self->polyphony)); + memset(self->importance, 0, sizeof(self->importance)); +} + +static void +_sci1_cleanup(sci1_song_iterator_t *it) +{ + sci1_sample_t *sample_seeker = it->next_sample; + while (sample_seeker) { + sci1_sample_t *old_sample = sample_seeker; + sample_seeker = sample_seeker->next; + sci_free(old_sample); + } + + _sci0_cleanup((sci0_song_iterator_t *)it); +} + +static int +_sci1_get_timepos(sci1_song_iterator_t *self) +{ + int max = 0; + int i; + + for (i = 0; i < self->channels_nr; i++) + if (self->channels[i].total_timepos > max) + max = self->channels[i].total_timepos; + + return max; +} + +/*****************************/ +/*-- Cleanup song iterator --*/ +/*****************************/ + + +static void +_cleanup_iterator_init(song_iterator_t *it) +{ +} + +static song_iterator_t * +_cleanup_iterator_handle_message(song_iterator_t *i, song_iterator_message_t msg) +{ + if (msg.recipient == _SIMSG_BASEMSG_PRINT + && msg.type == _SIMSG_BASEMSG_PRINT) { + print_tabs_id(msg.args[0].i, i->ID); + fprintf(stderr, "CLEANUP\n"); + } + + return NULL; +} + +static int +_cleanup_iterator_next(song_iterator_t *self, unsigned char *buf, int *result) +{ + /* Task: Return channel-notes-off for each channel */ + if (self->channel_mask) { + int bs = sci_ffs(self->channel_mask) - 1; + + self->channel_mask &= ~(1 << bs); + buf[0] = 0xb0 | bs; /* Controller */ + buf[1] = SCI_MIDI_CHANNEL_NOTES_OFF; + buf[2] = 0; /* Hmm... */ + *result = 3; + return 0; + } else + return SI_FINISHED; +} + +song_iterator_t * +new_cleanup_iterator(unsigned int channels) +{ + song_iterator_t *it = (song_iterator_t*)sci_malloc(sizeof(song_iterator_t)); + it->channel_mask = channels; + it->ID = 17; + it->flags = 0; + it->death_listeners_nr = 0; + + it->cleanup = NULL; + it->get_pcm_feed = NULL; + it->init = _cleanup_iterator_init; + it->handle_message = _cleanup_iterator_handle_message; + it->get_timepos = NULL; + it->next = _cleanup_iterator_next; + return it; +} + +/**********************************/ +/*-- Fast-forward song iterator --*/ +/**********************************/ + +static int +_ff_read_next_command(fast_forward_song_iterator_t *self, + byte *buf, int *result) +{ + int rv; + + if (self->delta <= 0) + return SI_MORPH; /* Did our duty */ + + while (1) { + rv = self->delegate->next(self->delegate, buf, result); + + if (rv > 0) { + /* Subtract from the delta we want to wait */ + self->delta -= rv; + + /* Done */ + if (self->delta < 0) + return -self->delta; + } + + if (rv <= 0) + return rv; + } +} + +static sfx_pcm_feed_t * +_ff_check_pcm(fast_forward_song_iterator_t *self) +{ + return self->delegate->get_pcm_feed(self->delegate); +} + +static song_iterator_t * +_ff_handle_message(fast_forward_song_iterator_t *self, + song_iterator_message_t msg) +{ + if (msg.recipient == _SIMSG_PLASTICWRAP) + switch (msg.type) { + + case _SIMSG_PLASTICWRAP_ACK_MORPH: + if (self->delta <= 0) { + song_iterator_t *it = self->delegate; + sci_free(self); + return it; + } + break; + + default: + BREAKPOINT(); + } + else if (msg.recipient == _SIMSG_BASE) { + switch (msg.type) { + + case _SIMSG_BASEMSG_CLONE: { + int tsize = sizeof(fast_forward_song_iterator_t); + fast_forward_song_iterator_t *clone = (fast_forward_song_iterator_t *)sci_malloc(tsize); + memcpy(clone, self, tsize); + songit_handle_message(&clone->delegate, msg); + return (song_iterator_t *) clone; + } + + case _SIMSG_BASEMSG_PRINT: + print_tabs_id(msg.args[0].i, self->ID); + fprintf(stderr, "PLASTICWRAP:\n"); + msg.args[0].i++; + songit_handle_message(&(self->delegate), msg); + break; + + default: + songit_handle_message(&(self->delegate), msg); + } + } else + songit_handle_message(&(self->delegate), msg); + + return NULL; +} + + +static void +_ff_init(fast_forward_song_iterator_t *self) +{ + return; +} + +static int +_ff_get_timepos(fast_forward_song_iterator_t *self) +{ + return self->delegate->get_timepos(self->delegate); +} + +song_iterator_t * +new_fast_forward_iterator(song_iterator_t *capsit, int delta) +{ + fast_forward_song_iterator_t *it = + (fast_forward_song_iterator_t*)sci_malloc(sizeof(fast_forward_song_iterator_t)); + + if (capsit == NULL) + { + free(it); + return NULL; + } + it->ID = 0; + + it->delegate = capsit; + it->delta = delta; + it->death_listeners_nr = 0; + + it->next = (int(*)(song_iterator_t *, unsigned char *, int *)) + _ff_read_next_command; + it->get_pcm_feed = (sfx_pcm_feed_t *(*)(song_iterator_t *)) + _ff_check_pcm; + it->handle_message = (song_iterator_t *(*)(song_iterator_t *, + song_iterator_message_t)) + _ff_handle_message; + it->get_timepos = (int(*)(song_iterator_t *))_ff_get_timepos; + it->init = (void(*)(song_iterator_t *)) + _ff_init; + it->cleanup = NULL; + it->channel_mask = capsit->channel_mask; + + + return (song_iterator_t *) it; +} + + +/********************/ +/*-- Tee iterator --*/ +/********************/ + + +static int +_tee_read_next_command(tee_song_iterator_t *it, unsigned char *buf, + int *result) +{ + static int ready_masks[2] = {TEE_LEFT_READY, TEE_RIGHT_READY}; + static int active_masks[2] = {TEE_LEFT_ACTIVE, TEE_RIGHT_ACTIVE}; + static int pcm_masks[2] = {TEE_LEFT_PCM, TEE_RIGHT_PCM}; + int i; + int retid; + +#ifdef DEBUG_TEE_ITERATOR + fprintf(stderr, "[Tee] %02x\n", it->status); +#endif + + if (!(it->status & (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE))) + /* None is active? */ + return SI_FINISHED; + + if (it->morph_deferred == TEE_MORPH_READY) + return SI_MORPH; + + if ((it->status & (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE)) + != (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE)) { + /* Not all are is active? */ + int which; +#ifdef DEBUG_TEE_ITERATOR + fprintf(stderr, "\tRequesting transformation...\n"); +#endif + if (it->status & TEE_LEFT_ACTIVE) + which = TEE_LEFT; + else if (it->status & TEE_RIGHT_ACTIVE) + which = TEE_RIGHT; + memcpy(buf, it->children[which].buf, MAX_BUF_SIZE); + *result = it->children[which].result; + it->morph_deferred = TEE_MORPH_READY; + return it->children[which].retval; + } + + /* First, check for unreported PCMs */ + for (i = TEE_LEFT; i <= TEE_RIGHT; i++) + if ((it->status & (ready_masks[i] | pcm_masks[i])) + == (ready_masks[i] | pcm_masks[i])) { + it->status &= ~ready_masks[i]; + return SI_PCM; + } + + for (i = TEE_LEFT; i <= TEE_RIGHT; i++) + if (!(it->status & ready_masks[i])) { + + /* Buffers aren't ready yet */ + it->children[i].retval = + songit_next(&(it->children[i].it), + it->children[i].buf, + &(it->children[i].result), + IT_READER_MASK_ALL + | IT_READER_MAY_FREE + | IT_READER_MAY_CLEAN); + + it->status |= ready_masks[i]; +#ifdef DEBUG_TEE_ITERATOR + fprintf(stderr, "\t Must check %d: %d\n", i, + it->children[i].retval); +#endif + + if (it->children[i].retval == SI_ABSOLUTE_CUE || + it->children[i].retval == SI_RELATIVE_CUE) + return it->children[i].retval; + if (it->children[i].retval == SI_FINISHED) { + it->status &= ~active_masks[i]; + /* Recurse to complete */ +#ifdef DEBUG_TEE_ITERATOR +fprintf(stderr, "\t Child %d signalled completion, recursing w/ status %02x\n", i, it->status); +#endif + return _tee_read_next_command(it, buf, result); + } else if (it->children[i].retval == SI_PCM) { + it->status |= pcm_masks[i]; + it->status &= ~ready_masks[i]; + return SI_PCM; + } + } + + + /* We've already handled PCM, MORPH and FINISHED, CUEs & LOOP remain */ + + retid = TEE_LEFT; + if ((it->children[TEE_LEFT].retval > 0) + /* Asked to delay */ + && (it->children[TEE_RIGHT].retval <= it->children[TEE_LEFT].retval)) + /* Is not delaying or not delaying as much */ + retid = TEE_RIGHT; + +#ifdef DEBUG_TEE_ITERATOR +fprintf(stderr, "\tl:%d / r:%d / chose %d\n", + it->children[TEE_LEFT].retval, it->children[TEE_RIGHT].retval, retid); +#endif +#if 0 + if (it->children[retid].retval == 0) { + /* Perform remapping, if neccessary */ + byte *buf = it->children[retid].buf; + if (*buf != SCI_MIDI_SET_SIGNAL + && *buf < 0xf0) { /* Not a generic command */ + int chan = *buf & 0xf; + int op = *buf & 0xf0; + + chan = it->children[retid].channel_remap[chan]; + + *buf = chan | op; + } + } +#endif + + /* Adjust delta times */ + if (it->children[retid].retval > 0 + && it->children[1-retid].retval > 0) { + if (it->children[1-retid].retval + == it->children[retid].retval) + /* If both children wait the same amount of time, + ** we have to re-fetch commands from both */ + it->status &= ~ready_masks[1-retid]; + else + /* If they don't, we can/must re-use the other + ** child's delay time */ + it->children[1-retid].retval + -= it->children[retid].retval; + } + + it->status &= ~ready_masks[retid]; + memcpy(buf, it->children[retid].buf, MAX_BUF_SIZE); + *result = it->children[retid].result; + + return it->children[retid].retval; +} + +static sfx_pcm_feed_t * +_tee_check_pcm(tee_song_iterator_t *it) +{ + static int pcm_masks[2] = {TEE_LEFT_PCM, TEE_RIGHT_PCM}; + int i; + + for (i = TEE_LEFT; i <= TEE_RIGHT; i++) + if (it->status & pcm_masks[i]) { + + it->status &= ~pcm_masks[i]; + return it->children[i].it-> + get_pcm_feed(it->children[i].it); + } + + return NULL; /* No iterator */ +} + +static song_iterator_t * +_tee_handle_message(tee_song_iterator_t *self, song_iterator_message_t msg) +{ + if (msg.recipient == _SIMSG_BASE) { + switch (msg.type) { + + case _SIMSG_BASEMSG_PRINT: + print_tabs_id(msg.args[0].i, self->ID); + fprintf(stderr, "TEE:\n"); + msg.args[0].i++; + break; /* And continue with our children */ + + case _SIMSG_BASEMSG_CLONE: { + tee_song_iterator_t *newit + = (tee_song_iterator_t*)sci_malloc(sizeof(tee_song_iterator_t)); + memcpy(newit, self, sizeof(tee_song_iterator_t)); + + if (newit->children[TEE_LEFT].it) + newit->children[TEE_LEFT].it = + songit_clone(newit->children[TEE_LEFT].it, msg.args[0].i); + if (newit->children[TEE_RIGHT].it) + newit->children[TEE_RIGHT].it = + songit_clone(newit->children[TEE_RIGHT].it, msg.args[0].i); + + return (song_iterator_t *) newit; + } + + default: + break; + } + } + + if (msg.recipient == _SIMSG_PLASTICWRAP) { + song_iterator_t *old_it; + switch (msg.type) { + + case _SIMSG_PLASTICWRAP_ACK_MORPH: + if (!(self->status & (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE))) { + songit_free((song_iterator_t *) self); + return NULL; + } else if (!(self->status & TEE_LEFT_ACTIVE)) { + if (self->may_destroy) + songit_free(self->children[TEE_LEFT].it); + old_it = self->children[TEE_RIGHT].it; + sci_free(self); + return old_it; + } else if (!(self->status & TEE_RIGHT_ACTIVE)) { + if (self->may_destroy) + songit_free(self->children[TEE_RIGHT].it); + old_it = self->children[TEE_LEFT].it; + sci_free(self); + return old_it; + } else { + sciprintf("[tee-iterator] WARNING:" + " Morphing without need\n"); + return (song_iterator_t *) self; + } + + default: + BREAKPOINT(); + } + } + + if (self->children[TEE_LEFT].it) + songit_handle_message(&(self->children[TEE_LEFT].it), msg); + if (self->children[TEE_RIGHT].it) + songit_handle_message(&(self->children[TEE_RIGHT].it), msg); + + return NULL; +} + +static void +_tee_init(tee_song_iterator_t *it) +{ + it->status = TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE; + it->children[TEE_LEFT].it->init(it->children[TEE_LEFT].it); + it->children[TEE_RIGHT].it->init(it->children[TEE_RIGHT].it); +} + +static void +_tee_free(tee_song_iterator_t *it) +{ + int i; + for (i = TEE_LEFT; i <= TEE_RIGHT; i++) + if (it->children[i].it && it->may_destroy) + songit_free(it->children[i].it); +} + +static void +songit_tee_death_notification(tee_song_iterator_t *self, + song_iterator_t *corpse) +{ + if (corpse == self->children[TEE_LEFT].it) { + self->status &= ~TEE_LEFT_ACTIVE; + self->children[TEE_LEFT].it = NULL; + } else if (corpse == self->children[TEE_RIGHT].it) { + self->status &= ~TEE_RIGHT_ACTIVE; + self->children[TEE_RIGHT].it = NULL; + } else { + BREAKPOINT(); + } +} + + +song_iterator_t * +songit_new_tee(song_iterator_t *left, song_iterator_t *right, int may_destroy) +{ + int i; + int firstfree = 1; /* First free channel */ + int incomplete_map = 0; + tee_song_iterator_t *it = (tee_song_iterator_t*)sci_malloc(sizeof(tee_song_iterator_t)); + + it->ID = 0; + + it->morph_deferred = TEE_MORPH_NONE; + it->status = TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE; + it->may_destroy = may_destroy; + + it->children[TEE_LEFT].it = left; + it->children[TEE_RIGHT].it = right; + it->death_listeners_nr = 0; + + /* By default, don't remap */ + for (i = 0; i < 16; i++) + it->children[TEE_LEFT].channel_remap[i] + = it->children[TEE_RIGHT].channel_remap[i] = i; + + /* Default to lhs channels */ + it->channel_mask = left->channel_mask; + for (i = 0; i < 16; i++) + if (it->channel_mask & (1 << i) & right->channel_mask + && (i != MIDI_RHYTHM_CHANNEL) /* Share rhythm */) { /*conflict*/ + while ((firstfree == MIDI_RHYTHM_CHANNEL) + /* Either if it's the rhythm channel or if it's taken */ + || (firstfree < MIDI_CHANNELS + && ((1 << firstfree) & it->channel_mask))) + ++firstfree; + + if (firstfree == MIDI_CHANNELS) { + incomplete_map = 1; + fprintf(stderr, "[songit-tee <%08lx,%08lx>] " + "Could not remap right channel #%d:" + " Out of channels\n", + left->ID, right->ID, i); + } else { + it->children[TEE_RIGHT].channel_remap[i] + = firstfree; + + it->channel_mask |= (1 << firstfree); + } + } +#ifdef DEBUG_TEE_ITERATOR + if (incomplete_map) { + int c; + fprintf(stderr, "[songit-tee <%08lx,%08lx>] Channels:" + " %04x <- %04x | %04x\n", + left->ID, right->ID, + it->channel_mask, + left->channel_mask, right->channel_mask); + for (c =0 ; c < 2; c++) + for (i =0 ; i < 16; i++) + fprintf(stderr, " map [%d][%d] -> %d\n", + c, i, it->children[c].channel_remap[i]); + } +#endif + + + it->next = (int(*)(song_iterator_t *, unsigned char *, int *)) + _tee_read_next_command; + + it->get_pcm_feed = (sfx_pcm_feed_t*(*)(song_iterator_t *)) + _tee_check_pcm; + + it->handle_message = (song_iterator_t *(*)(song_iterator_t *, + song_iterator_message_t)) + _tee_handle_message; + + it->init = (void(*)(song_iterator_t *)) + _tee_init; + + it->get_timepos = NULL; + + song_iterator_add_death_listener((song_iterator_t *)it, + left, (void (*)(void *, void*)) + songit_tee_death_notification); + song_iterator_add_death_listener((song_iterator_t *)it, + right, (void (*)(void *, void*)) + songit_tee_death_notification); + + it->cleanup = NULL; + + return (song_iterator_t *) it; +} + + +/*************************************/ +/*-- General purpose functionality --*/ +/*************************************/ + +int +songit_next(song_iterator_t **it, unsigned char *buf, int *result, int mask) +{ + int retval; + + if (!*it) + return SI_FINISHED; + + do { + retval = (*it)->next(*it, buf, result); + if (retval == SI_MORPH) { + fprintf(stderr, " Morphing %p (stored at %p)\n", *it, it); + if (!SIMSG_SEND((*it), SIMSG_ACK_MORPH)) { + BREAKPOINT(); + } else fprintf(stderr, "SI_MORPH successful\n"); + } + + if (retval == SI_FINISHED) + fprintf(stderr, "[song-iterator] Song finished. mask = %04x, cm=%04x\n", + mask, (*it)->channel_mask); + if (retval == SI_FINISHED + && (mask & IT_READER_MAY_CLEAN) + && (*it)->channel_mask) { /* This last test will fail + ** with a terminated + ** cleanup iterator */ + int channel_mask = (*it)->channel_mask; + + if (mask & IT_READER_MAY_FREE) + songit_free(*it); + *it = new_cleanup_iterator(channel_mask); + retval = -9999; /* Continue */ + } + } while (! ( /* Until one of the following holds */ + (retval > 0 && (mask & IT_READER_MASK_DELAY)) + || (retval == 0 && (mask & IT_READER_MASK_MIDI)) + || (retval == SI_LOOP && (mask & IT_READER_MASK_LOOP)) + || (retval == SI_ABSOLUTE_CUE && + (mask & IT_READER_MASK_CUE)) + || (retval == SI_RELATIVE_CUE && + (mask & IT_READER_MASK_CUE)) + || (retval == SI_PCM && (mask & IT_READER_MASK_PCM)) + || (retval == SI_FINISHED) + )); + + if (retval == SI_FINISHED + && (mask & IT_READER_MAY_FREE)) { + songit_free(*it); + *it = NULL; + } + + return retval; +} + + + +song_iterator_t * +songit_new(unsigned char *data, unsigned int size, int type, songit_id_t id) +{ + base_song_iterator_t *it; + int i; + + if (!data || size < 22) { + fprintf(stderr, SIPFX "Attempt to instantiate song iterator for null" + " song data\n"); + return NULL; + } + + + switch (type) { + + case SCI_SONG_ITERATOR_TYPE_SCI0: + /**-- Playing SCI0 sound resources --**/ + it = (base_song_iterator_t*)sci_malloc(sizeof(sci0_song_iterator_t)); + it->channel_mask = 0xffff; /* Allocate all channels by default */ + + for (i = 0; i < MIDI_CHANNELS; i++) + it->polyphony[i] = data[1 + (i << 1)]; + + it->next = (int(*)(song_iterator_t *, unsigned char *, int *)) + _sci0_read_next_command; + it->get_pcm_feed = (sfx_pcm_feed_t*(*)(song_iterator_t *)) + _sci0_check_pcm; + it->handle_message = (song_iterator_t *(*)(song_iterator_t *, song_iterator_message_t)) + _sci0_handle_message; + it->init = (void(*)(song_iterator_t *))_sci0_init; + it->cleanup = (void(*)(song_iterator_t *))_sci0_cleanup; + ((sci0_song_iterator_t *)it)->channel.state + = SI_STATE_UNINITIALISED; + it->get_timepos = (int(*)(song_iterator_t *))_sci0_get_timepos; + break; + + case SCI_SONG_ITERATOR_TYPE_SCI1: + /**-- SCI01 or later sound resource --**/ + it = (base_song_iterator_t*)sci_malloc(sizeof(sci1_song_iterator_t)); + it->channel_mask = 0; /* Defer channel allocation */ + + for (i = 0; i < MIDI_CHANNELS; i++) + it->polyphony[i] = 0; /* Unknown */ + + it->next = (int(*)(song_iterator_t *, unsigned char *, int *)) + _sci1_read_next_command; + it->get_pcm_feed = (sfx_pcm_feed_t*(*)(song_iterator_t *)) + _sci1_get_pcm; + it->handle_message = (song_iterator_t *(*)(song_iterator_t *, song_iterator_message_t)) + _sci1_handle_message; + it->init = (void(*)(song_iterator_t *))_sci1_init; + it->cleanup = (void(*)(song_iterator_t *))_sci1_cleanup; + it->get_timepos = (int(*)(song_iterator_t *))_sci1_get_timepos; + break; + + default: + /**-- Invalid/unsupported sound resources --**/ + fprintf(stderr, SIPFX "Attempt to instantiate invalid/unknown" + " song iterator type %d\n", type); + return NULL; + } + it->ID = id; + + it->death_listeners_nr = 0; + + it->data = (unsigned char*)sci_refcount_memdup(data, size); + it->size = size; + + it->init((song_iterator_t *) it); + + return (song_iterator_t *) it; +} + +void +songit_free(song_iterator_t *it) +{ + if (it) { + int i; + + if (it->cleanup) + it->cleanup(it); + + for (i = 0; i < it->death_listeners_nr; i++) + it->death_listeners[i].notify(it->death_listeners[i].self, it); + + sci_free(it); + } +} + +song_iterator_message_t +songit_make_message(songit_id_t id, int recipient, int type, int a1, int a2) +{ + song_iterator_message_t rv; + rv.ID = id; + rv.recipient = recipient; + rv.type = type; + rv.args[0].i = a1; + rv.args[1].i = a2; + + return rv; +} + +song_iterator_message_t +songit_make_ptr_message(songit_id_t id, int recipient, int type, void * a1, int a2) +{ + song_iterator_message_t rv; + rv.ID = id; + rv.recipient = recipient; + rv.type = type; + rv.args[0].p = a1; + rv.args[1].i = a2; + + return rv; +} + + +int +songit_handle_message(song_iterator_t **it_reg_p, song_iterator_message_t msg) +{ + song_iterator_t *it = *it_reg_p; + song_iterator_t *newit; + + newit = it->handle_message(it, msg); + + if (!newit) + return 0; /* Couldn't handle */ + + *it_reg_p = newit; /* Might have self-morphed */ + return 1; +} + +song_iterator_t * +songit_clone(song_iterator_t *it, int delta) +{ + SIMSG_SEND(it, SIMSG_CLONE(delta)); + it->death_listeners_nr = 0; + it->flags |= SONGIT_FLAG_CLONE; + return it; +} + +void +song_iterator_add_death_listener(song_iterator_t *it, + void *client, + void (*notify) (void *self, void *notifier)) +{ + if (it->death_listeners_nr >= SONGIT_MAX_LISTENERS) { + fprintf(stderr, "FATAL: Too many death listeners for song" + " iterator\n"); + BREAKPOINT(); + exit(1); + } + + it->death_listeners[it->death_listeners_nr].notify = notify; + it->death_listeners[it->death_listeners_nr].self = client; + + it->death_listeners_nr++; +} + +void +song_iterator_remove_death_listener(song_iterator_t *it, + void *client) +{ + int i; + for (i = 0; i < it->death_listeners_nr; i++) { + if (it->death_listeners[i].self == client) { + --it->death_listeners_nr; + + /* Overwrite, if this wasn't the last one */ + if (i+1 < it->death_listeners_nr) + it->death_listeners[i] + = it->death_listeners[it->death_listeners_nr]; + + return; + } + } + + fprintf(stderr, "FATAL: Could not remove death listener from " + "song iterator\n"); + BREAKPOINT(); + exit(1); +} + + +song_iterator_t * +sfx_iterator_combine(song_iterator_t *it1, song_iterator_t *it2) +{ + if (it1 == NULL) + return it2; + if (it2 == NULL) + return it1; + + /* Both are non-NULL: */ + return songit_new_tee(it1, it2, 1); /* 'may destroy' */ +} diff --git a/engines/sci/sfx/lists/GM.txt b/engines/sci/sfx/lists/GM.txt new file mode 100644 index 0000000000..eea2510447 --- /dev/null +++ b/engines/sci/sfx/lists/GM.txt @@ -0,0 +1,177 @@ +/*000 00*/ "Acoustic Grand Piano", +/*001 01*/ "Bright Acoustic Piano", +/*002 02*/ "Electric Grand Piano", +/*003 03*/ "Honky-tonk Piano", +/*004 04*/ "Electric Piano 1", +/*005 05*/ "Electric Piano 2", +/*006 06*/ "Harpsichord", +/*007 07*/ "Clavinet", +/*008 08*/ "Celesta", +/*009 09*/ "Glockenspiel", +/*010 0A*/ "Music Box", +/*011 0B*/ "Vibraphone", +/*012 0C*/ "Marimba", +/*013 0D*/ "Xylophone", +/*014 0E*/ "Tubular Bells", +/*015 0F*/ "Dulcimer", +/*016 10*/ "Drawbar Organ", +/*017 11*/ "Percussive Organ", +/*018 12*/ "Rock Organ", +/*019 13*/ "Church Organ", +/*020 14*/ "Reed Organ", +/*021 15*/ "Accordion", +/*022 16*/ "Harmonica", +/*023 17*/ "Tango Accordion", +/*024 18*/ "Acoustic Guitar (nylon)", +/*025 19*/ "Acoustic Guitar (steel)", +/*026 1A*/ "Electric Guitar (jazz)", +/*027 1B*/ "Electric Guitar (clean)", +/*028 1C*/ "Electric Guitar (muted)", +/*029 1D*/ "Overdriven Guitar", +/*030 1E*/ "Distortion Guitar", +/*031 1F*/ "Guitar Harmonics", +/*032 20*/ "Acoustic Bass", +/*033 21*/ "Electric Bass (finger)", +/*034 22*/ "Electric Bass (pick)", +/*035 23*/ "Fretless Bass", +/*036 24*/ "Slap Bass 1", +/*037 25*/ "Slap Bass 2", +/*038 26*/ "Synth Bass 1", +/*039 27*/ "Synth Bass 2", +/*040 28*/ "Violin", +/*041 29*/ "Viola", +/*042 2A*/ "Cello", +/*043 2B*/ "Contrabass", +/*044 2C*/ "Tremolo Strings", +/*045 2D*/ "Pizzicato Strings", +/*046 2E*/ "Orchestral Harp", +/*047 2F*/ "Timpani", +/*048 30*/ "String Ensemble 1", +/*049 31*/ "String Ensemble 2", +/*050 32*/ "SynthStrings 1", +/*051 33*/ "SynthStrings 2", +/*052 34*/ "Choir Aahs", +/*053 35*/ "Voice Oohs", +/*054 36*/ "Synth Voice", +/*055 37*/ "Orchestra Hit", +/*056 38*/ "Trumpet", +/*057 39*/ "Trombone", +/*058 3A*/ "Tuba", +/*059 3B*/ "Muted Trumpet", +/*060 3C*/ "French Horn", +/*061 3D*/ "Brass Section", +/*062 3E*/ "SynthBrass 1", +/*063 3F*/ "SynthBrass 2", + +/*064 40*/ "Soprano Sax", +/*065 41*/ "Alto Sax", +/*066 42*/ "Tenor Sax", +/*067 43*/ "Baritone Sax", +/*068 44*/ "Oboe", +/*069 45*/ "English Horn", +/*070 46*/ "Bassoon", +/*071 47*/ "Clarinet", +/*072 48*/ "Piccolo", +/*073 49*/ "Flute", +/*074 4A*/ "Recorder", +/*075 4B*/ "Pan Flute", +/*076 4C*/ "Blown Bottle", +/*077 4D*/ "Shakuhachi", +/*078 4E*/ "Whistle", +/*079 4F*/ "Ocarina", +/*080 50*/ "Lead 1 (square)", +/*081 51*/ "Lead 2 (sawtooth)", +/*082 52*/ "Lead 3 (calliope)", +/*083 53*/ "Lead 4 (chiff)", +/*084 54*/ "Lead 5 (charang)", +/*085 55*/ "Lead 6 (voice)", +/*086 56*/ "Lead 7 (fifths)", +/*087 57*/ "Lead 8 (bass+lead)", +/*088 58*/ "Pad 1 (new age)", +/*089 59*/ "Pad 2 (warm)", +/*090 5A*/ "Pad 3 (polysynth)", +/*091 5B*/ "Pad 4 (choir)", +/*092 5C*/ "Pad 5 (bowed)", +/*093 5D*/ "Pad 6 (metallic)", +/*094 5E*/ "Pad 7 (halo)", +/*095 5F*/ "Pad 8 (sweep)", +/*096 60*/ "FX 1 (rain)", +/*097 61*/ "FX 2 (soundtrack)", +/*098 62*/ "FX 3 (crystal)", +/*099 63*/ "FX 4 (atmosphere)", +/*100 64*/ "FX 5 (brightness)", +/*101 65*/ "FX 6 (goblins)", +/*102 66*/ "FX 7 (echoes)", +/*103 67*/ "FX 8 (sci-fi)", +/*104 68*/ "Sitar", +/*105 69*/ "Banjo", +/*106 6A*/ "Shamisen", +/*107 6B*/ "Koto", +/*108 6C*/ "Kalimba", +/*109 6D*/ "Bag pipe", +/*110 6E*/ "Fiddle", +/*111 6F*/ "Shannai", +/*112 70*/ "Tinkle Bell", +/*113 71*/ "Agogo", +/*114 72*/ "Steel Drums", +/*115 73*/ "Woodblock", +/*116 74*/ "Taiko Drum", +/*117 75*/ "Melodic Tom", +/*118 76*/ "Synth Drum", +/*119 77*/ "Reverse Cymbal", +/*120 78*/ "Guitar Fret Noise", +/*121 79*/ "Breath Noise", +/*122 7A*/ "Seashore", +/*123 7B*/ "Bird Tweet", +/*124 7C*/ "Telephone Ring", +/*125 7D*/ "Helicopter", +/*126 7E*/ "Applause", +/*127 7F*/ "Gunshot" + +/*035 23*/ "Acoustic Bass Drum", +/*036 24*/ "Bass Drum 1", +/*037 25*/ "Side Stick", +/*038 26*/ "Acoustic Snare", +/*039 27*/ "Hand Clap", +/*040 28*/ "Electric Snare", +/*041 29*/ "Low Floor Tom", +/*042 2A*/ "Closed Hi-Hat", +/*043 2B*/ "High Floor Tom", +/*044 2C*/ "Pedal Hi-Hat", +/*045 2D*/ "Low Tom", +/*046 2E*/ "Open Hi-Hat", +/*047 2F*/ "Low-Mid Tom", +/*048 30*/ "Hi-Mid Tom", +/*049 31*/ "Crash Cymbal 1", +/*050 32*/ "High Tom", +/*051 33*/ "Ride Cymbal 1", +/*052 34*/ "Chinese Cymbal", +/*053 35*/ "Ride Bell", +/*054 36*/ "Tambourine", +/*055 37*/ "Splash Cymbal", +/*056 38*/ "Cowbell", +/*057 39*/ "Crash Cymbal 2", +/*058 3A*/ "Vibraslap", +/*059 3B*/ "Ride Cymbal 2", +/*060 3C*/ "Hi Bongo", +/*061 3D*/ "Low Bongo", +/*062 3E*/ "Mute Hi Conga", +/*063 3F*/ "Open Hi Conga", +/*064 40*/ "Low Conga", +/*065 41*/ "High Timbale", +/*066 42*/ "Low Timbale", +/*067 43*/ "High Agogo", +/*068 44*/ "Low Agogo", +/*069 45*/ "Cabasa", +/*070 46*/ "Maracas", +/*071 47*/ "Short Whistle", +/*072 48*/ "Long Whistle", +/*073 49*/ "Short Guiro", +/*074 4A*/ "Long Guiro", +/*075 4B*/ "Claves", +/*076 4C*/ "Hi Wood Block", +/*077 4D*/ "Low Wood Block", +/*078 4E*/ "Mute Cuica", +/*079 4F*/ "Open Cuica", +/*080 50*/ "Mute Triangle", +/*081 51*/ "Open Triangle" diff --git a/engines/sci/sfx/lists/gm_patches.c b/engines/sci/sfx/lists/gm_patches.c new file mode 100644 index 0000000000..b959a87461 --- /dev/null +++ b/engines/sci/sfx/lists/gm_patches.c @@ -0,0 +1,198 @@ +/*************************************************************************** + gm_patches.c Copyright (C) 2000 Rickard Lind + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +char *GM_Patch[128] = { +/*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" }; + +char *GM_RhythmKey[47] = { +/*035*/ "Acoustic Bass Drum", +/*036*/ "Bass Drum 1", +/*037*/ "Side Stick", +/*038*/ "Acoustic Snare", +/*039*/ "Hand Clap", +/*040*/ "Electric Snare", +/*041*/ "Low Floor Tom", +/*042*/ "Closed Hi-Hat", +/*043*/ "High Floor Tom", +/*044*/ "Pedal Hi-Hat", +/*045*/ "Low Tom", +/*046*/ "Open Hi-Hat", +/*047*/ "Low-Mid Tom", +/*048*/ "Hi-Mid Tom", +/*049*/ "Crash Cymbal 1", +/*050*/ "High Tom", +/*051*/ "Ride Cymbal 1", +/*052*/ "Chinese Cymbal", +/*053*/ "Ride Bell", +/*054*/ "Tambourine", +/*055*/ "Splash Cymbal", +/*056*/ "Cowbell", +/*057*/ "Crash Cymbal 2", +/*058*/ "Vibraslap", +/*059*/ "Ride Cymbal 2", +/*060*/ "Hi Bongo", +/*061*/ "Low Bongo", +/*062*/ "Mute Hi Conga", +/*063*/ "Open Hi Conga", +/*064*/ "Low Conga", +/*065*/ "High Timbale", +/*066*/ "Low Timbale", +/*067*/ "High Agogo", +/*068*/ "Low Agogo", +/*069*/ "Cabasa", +/*070*/ "Maracas", +/*071*/ "Short Whistle", +/*072*/ "Long Whistle", +/*073*/ "Short Guiro", +/*074*/ "Long Guiro", +/*075*/ "Claves", +/*076*/ "Hi Wood Block", +/*077*/ "Low Wood Block", +/*078*/ "Mute Cuica", +/*079*/ "Open Cuica", +/*080*/ "Mute Triangle" +/*081*/ "Open Triangle" }; diff --git a/engines/sci/sfx/lists/mt32_timbres.c b/engines/sci/sfx/lists/mt32_timbres.c new file mode 100644 index 0000000000..776ff5dbb7 --- /dev/null +++ b/engines/sci/sfx/lists/mt32_timbres.c @@ -0,0 +1,181 @@ +/*************************************************************************** + mt_32_timbres.c Copyright (C) 2000 Rickard Lind + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +char *MT32_Timbre[128] = { +/*000*/ "AcouPiano1", +/*001*/ "AcouPiano2", +/*002*/ "AcouPiano3", +/*003*/ "ElecPiano1", +/*004*/ "ElecPiano2", +/*005*/ "ElecPiano3", +/*006*/ "ElecPiano4", +/*007*/ "Honkytonk ", +/*008*/ "Elec Org 1", +/*009*/ "Elec Org 2", +/*010*/ "Elec Org 3", +/*011*/ "Elec Org 4", +/*012*/ "Pipe Org 1", +/*013*/ "Pipe Org 2", +/*014*/ "Pipe Org 3", +/*015*/ "Accordion ", +/*016*/ "Harpsi 1 ", +/*017*/ "Harpsi 2 ", +/*018*/ "Harpsi 3 ", +/*019*/ "Clavi 1 ", +/*020*/ "Clavi 2 ", +/*021*/ "Clavi 3 ", +/*022*/ "Celesta 1 ", +/*023*/ "Celesta 2 ", +/*024*/ "Syn Brass1", +/*025*/ "Syn Brass2", +/*026*/ "Syn Brass3", +/*027*/ "Syn Brass4", +/*028*/ "Syn Bass 1", +/*029*/ "Syn Bass 2", +/*030*/ "Syn Bass 3", +/*031*/ "Syn Bass 4", +/*032*/ "Fantasy ", +/*033*/ "Harmo Pan ", +/*034*/ "Chorale ", +/*035*/ "Glasses ", +/*036*/ "Soundtrack", +/*037*/ "Atmosphere", +/*038*/ "Warm Bell ", +/*039*/ "Funny Vox ", +/*040*/ "Echo Bell ", +/*041*/ "Ice Rain ", +/*042*/ "Oboe 2001 ", +/*043*/ "Echo Pan ", +/*044*/ "DoctorSolo", +/*045*/ "Schooldaze", +/*046*/ "BellSinger", +/*047*/ "SquareWave", +/*048*/ "Str Sect 1", +/*049*/ "Str Sect 2", +/*050*/ "Str Sect 3", +/*051*/ "Pizzicato ", +/*052*/ "Violin 1 ", +/*053*/ "Violin 2 ", +/*054*/ "Cello 1 ", +/*055*/ "Cello 2 ", +/*056*/ "Contrabass", +/*057*/ "Harp 1 ", +/*058*/ "Harp 2 ", +/*059*/ "Guitar 1 ", +/*060*/ "Guitar 2 ", +/*061*/ "Elec Gtr 1", +/*062*/ "Elec Gtr 2", +/*063*/ "Sitar ", +/*064*/ "Acou Bass1", +/*065*/ "Acou Bass2", +/*066*/ "Elec Bass1", +/*067*/ "Elec Bass2", +/*068*/ "Slap Bass1", +/*069*/ "Slap Bass2", +/*070*/ "Fretless 1", +/*071*/ "Fretless 2", +/*072*/ "Flute 1 ", +/*073*/ "Flute 2 ", +/*074*/ "Piccolo 1 ", +/*075*/ "Piccolo 2 ", +/*076*/ "Recorder ", +/*077*/ "Panpipes ", +/*078*/ "Sax 1 ", +/*079*/ "Sax 2 ", +/*080*/ "Sax 3 ", +/*081*/ "Sax 4 ", +/*082*/ "Clarinet 1", +/*083*/ "Clarinet 2", +/*084*/ "Oboe ", +/*085*/ "Engl Horn ", +/*086*/ "Bassoon ", +/*087*/ "Harmonica ", +/*088*/ "Trumpet 1 ", +/*089*/ "Trumpet 2 ", +/*090*/ "Trombone 1", +/*091*/ "Trombone 2", +/*092*/ "Fr Horn 1 ", +/*093*/ "Fr Horn 2 ", +/*094*/ "Tuba ", +/*095*/ "Brs Sect 1", +/*096*/ "Brs Sect 2", +/*097*/ "Vibe 1 ", +/*098*/ "Vibe 2 ", +/*099*/ "Syn Mallet", +/*100*/ "Wind Bell ", +/*101*/ "Glock ", +/*102*/ "Tube Bell ", +/*103*/ "Xylophone ", +/*104*/ "Marimba ", +/*105*/ "Koto ", +/*106*/ "Sho ", +/*107*/ "Shakuhachi", +/*108*/ "Whistle 1 ", +/*109*/ "Whistle 2 ", +/*110*/ "BottleBlow", +/*111*/ "BreathPipe", +/*112*/ "Timpani ", +/*113*/ "MelodicTom", +/*114*/ "Deep Snare", +/*115*/ "Elec Perc1", +/*116*/ "Elec Perc2", +/*117*/ "Taiko ", +/*118*/ "Taiko Rim ", +/*119*/ "Cymbal ", +/*120*/ "Castanets ", +/*121*/ "Triangle ", +/*122*/ "Orche Hit ", +/*123*/ "Telephone ", +/*124*/ "Bird Tweet", +/*125*/ "OneNoteJam", +/*126*/ "WaterBells", +/*127*/ "JungleTune" }; + +char *MT32_RhythmTimbre[30] = { +/*00*/ "Acou BD ", +/*01*/ "Acou SD ", +/*02*/ "Acou HiTom", +/*03*/ "AcouMidTom", +/*04*/ "AcouLowTom", +/*05*/ "Elec SD ", +/*06*/ "Clsd HiHat", +/*07*/ "OpenHiHat1", +/*08*/ "Crash Cym ", +/*09*/ "Ride Cym ", +/*10*/ "Rim Shot ", +/*11*/ "Hand Clap ", +/*12*/ "Cowbell ", +/*13*/ "Mt HiConga", +/*14*/ "High Conga", +/*15*/ "Low Conga ", +/*16*/ "Hi Timbale", +/*17*/ "LowTimbale", +/*18*/ "High Bongo", +/*19*/ "Low Bongo ", +/*20*/ "High Agogo", +/*21*/ "Low Agogo ", +/*22*/ "Tambourine", +/*23*/ "Claves ", +/*24*/ "Maracas ", +/*25*/ "SmbaWhis L", +/*26*/ "SmbaWhis S", +/*27*/ "Cabasa ", +/*28*/ "Quijada ", +/*29*/ "OpenHiHat2" }; diff --git a/engines/sci/sfx/mixer.h b/engines/sci/sfx/mixer.h new file mode 100644 index 0000000000..7cf2aae397 --- /dev/null +++ b/engines/sci/sfx/mixer.h @@ -0,0 +1,119 @@ +/*************************************************************************** + sfx_mixer.h Copyright (C) 2003,04 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef _SFX_MIXER_H_ +#define _SFX_MIXER_H_ + +#include + + +#define SFX_PCM_FEED_MODE_ALIVE 0 +#define SFX_PCM_FEED_MODE_DEAD 1 + +struct twochannel_data { + int left, right; +}; + +typedef struct { + sfx_pcm_feed_t *feed; + + /* The following fields are for use by the mixer only and must not be + ** touched by pcm_feed code. */ + byte *buf; /* dynamically allocated buffer for this feed, used in some circumstances. */ + int buf_size; /* Number of frames that fit into the buffer */ + sfx_pcm_urat_t spd; /* source frames per destination frames */ + sfx_pcm_urat_t scount; /* Frame counter, backed up in between calls */ + int frame_bufstart; /* Left-over frames at the beginning of the buffer */ + int mode; /* Whether the feed is alive or pending destruction */ + + int pending_review; /* Timestamp needs to be checked for this stream */ + struct twochannel_data ch_old, ch_new; /* Intermediate results of output computation */ +} sfx_pcm_feed_state_t; + + +typedef struct _sfx_pcm_mixer { + /* Mixers are the heart of all matters PCM. They take PCM data from subscribed feeds, + ** mix it (hence the name) and ask the pcm device they are attached to to play the + ** result. */ + + const char *name; + const char *version; + + int (*init)(struct _sfx_pcm_mixer *self, sfx_pcm_device_t *device); + /* Initialises the mixer + ** Parameters: (sfx_pcm_mixer_t *) self: Self reference + ** (sfx_pcm_device_t *) device: An _already initialised_ PCM output driver + ** Returns : (int) SFX_OK on success, SFX_ERROR otherwise + */ + + void (*exit)(struct _sfx_pcm_mixer *self); + /* Uninitialises the mixer + ** Parameters: (sfx_pcm_mixer_t *) self: Self reference + ** Also uninitialises all feeds and the attached output device. + */ + + void (*subscribe)(struct _sfx_pcm_mixer *self, sfx_pcm_feed_t *feed); + /* Subscribes the mixer to a new feed + ** Parameters: (sfx_pcm_mixer_t *) self: Self reference + ** (sfx_pcm_feed_t *) feed: The feed to subscribe to + */ + + void (*pause)(struct _sfx_pcm_mixer *self); + /* Pauses the processing of input and output + */ + + void (*resume)(struct _sfx_pcm_mixer *self); + /* Resumes the processing of input and output after a pause + */ + + int (*process)(struct _sfx_pcm_mixer *self); + /* Processes all feeds, mixes their results, and passes everything to the output device + ** Returns : (int) SFX_OK on success, SFX_ERROR otherwise (output device error or + ** internal assertion failure) + ** Effects : All feeds are poll()ed, and the device is asked to output(). Buffer size + ** depends on the time that has passed since the last call to process(), if + ** any. + */ + + int feeds_nr; + int feeds_allocd; + sfx_pcm_feed_state_t *feeds; + sfx_pcm_device_t *dev; + + void *private_bits; +} sfx_pcm_mixer_t; + +sfx_pcm_mixer_t * +sfx_pcm_find_mixer(char *name); +/* Looks up a mixer by name, or a default mixer +** Parameters: (char *) name: Name of the mixer to look for, or NULL to +** take a default +*/ + +extern sfx_pcm_mixer_t *mixer; /* _THE_ global pcm mixer */ + +#endif /* !defined(_SFX_MIXER_H_) */ diff --git a/engines/sci/sfx/mixer/Makefile.am b/engines/sci/sfx/mixer/Makefile.am new file mode 100644 index 0000000000..684d40a4ba --- /dev/null +++ b/engines/sci/sfx/mixer/Makefile.am @@ -0,0 +1,6 @@ +EXTRA_DIST = dc.c +noinst_LIBRARIES = libscimixer.a +INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@ +libscimixer_a_SOURCES = mixers.c soft.c +test_LDADD = libscimixer.a ../../scicore/libscicore.a +check_PROGRAMS = test diff --git a/engines/sci/sfx/mixer/dc.c b/engines/sci/sfx/mixer/dc.c new file mode 100644 index 0000000000..52b3ab5cbb --- /dev/null +++ b/engines/sci/sfx/mixer/dc.c @@ -0,0 +1,329 @@ +/*************************************************************************** + dc.c Copyright (C) 2005 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +#include "../mixer.h" +#include +#include +#include +#include + +#define FEED_MODE_ALIVE 0 +#define FEED_MODE_IDLE 1 +#define FEED_MODE_DIEING 2 +#define FEED_MODE_DEAD 3 +#define FEED_MODE_RESTART 4 + +typedef struct feed_state { + /* Queue entry. */ + TAILQ_ENTRY(feed_state) entry; + + /* Whether feed is alive or dead. */ + int mode; + + /* Blank gap in frames. */ + int gap; + + /* Stream handle. */ + snd_stream_hnd_t handle; + + /* Feed. */ + sfx_pcm_feed_t *feed; + + /* Timestamp of next frame requested by stream driver. */ + sfx_timestamp_t time; +} feed_state_t; + +TAILQ_HEAD(feed_list, feed_state) feeds; + +/* Buffer size in samples. */ +#define BUF_SIZE 0x4000 + +static char buf[BUF_SIZE * 2]; + +static feed_state_t * +find_feed_state(snd_stream_hnd_t hnd) +{ + feed_state_t *state; + TAILQ_FOREACH(state, &feeds, entry) { + if (state->handle == hnd) + return state; + } + + return NULL; +} + +static void +query_timestamp(feed_state_t *state) +{ + sfx_pcm_feed_t *feed = state->feed; + + if (feed->get_timestamp) { + sfx_timestamp_t stamp; + int val = feed->get_timestamp(feed, &stamp); + + switch (val) { + case PCM_FEED_TIMESTAMP: + state->gap = sfx_timestamp_frame_diff(stamp, state->time); + + if (state->gap >= 0) + state->mode = FEED_MODE_ALIVE; + else { + long secs, usecs; + + state->mode = FEED_MODE_RESTART; + sci_gettime(&secs, &usecs); + state->time = sfx_new_timestamp(secs, usecs, feed->conf.rate); + state->gap = sfx_timestamp_frame_diff(stamp, state->time); + + if (state->gap < 0) + state->gap = 0; + } + break; + case PCM_FEED_IDLE: + state->mode = FEED_MODE_IDLE; + break; + case PCM_FEED_EMPTY: + state->mode = FEED_MODE_DIEING; + state->gap = BUF_SIZE; + } + } else { + state->mode = FEED_MODE_DIEING; + state->gap = BUF_SIZE; + } +} + +void +U8_to_S16(char *buf, int frames, int stereo) +{ + int samples = frames * (stereo ? 2 : 1); + int i; + + for (i = samples - 1; i >= 0; i--) { + buf[i * 2 + 1] = (unsigned char) buf[i] - 128; + buf[i * 2] = 0; + } +} + +static void * +callback(snd_stream_hnd_t hnd, sfx_timestamp_t timestamp, int bytes_req, int *bytes_recv) +{ + feed_state_t *state = find_feed_state(hnd); + sfx_pcm_feed_t *feed; + int channels, frames_req; + int frames_recv = 0; + + assert(state); + + state->time = timestamp; + feed = state->feed; + channels = feed->conf.stereo == SFX_PCM_MONO ? 1 : 2; + frames_req = bytes_req / 2 / channels; + + while (frames_req != frames_recv) { + int frames_left = frames_req - frames_recv; + char *buf_pos = buf + frames_recv * channels * 2; + + if (state->mode == FEED_MODE_IDLE) + query_timestamp(state); + + if (state->mode == FEED_MODE_IDLE) { + memset(buf_pos, 0, frames_left * channels * 2); + + state->time = sfx_timestamp_add(state->time, frames_left); + break; + } + + if (state->gap) { + int frames = state->gap; + + if (frames > frames_left) + frames = frames_left; + + memset(buf_pos, 0, frames * channels * 2); + + state->gap -= frames; + frames_recv += frames; + state->time = sfx_timestamp_add(state->time, frames); + if (!state->gap && state->mode == FEED_MODE_DIEING) { + state->mode = FEED_MODE_DEAD; + break; + } + } else { + int frames = feed->poll(feed, buf_pos, frames_left); + + if (feed->conf.format == SFX_PCM_FORMAT_U8) + U8_to_S16(buf_pos, frames, feed->conf.stereo != SFX_PCM_MONO); + + frames_recv += frames; + state->time = sfx_timestamp_add(state->time, frames); + + if (frames < frames_left) + query_timestamp(state); + } + } + + *bytes_recv = bytes_req; + return buf; +} + +static int +mix_init(sfx_pcm_mixer_t *self, sfx_pcm_device_t *device) +{ + if (snd_stream_init() < 0) { + fprintf(stderr, "[dc-mixer] Failed to initialize streaming sound driver\n"); + return SFX_ERROR; + } + + TAILQ_INIT(&feeds); + + return SFX_OK; +} + +static void +mix_subscribe(sfx_pcm_mixer_t *self, sfx_pcm_feed_t *feed) +{ + feed_state_t *state = sci_malloc(sizeof(feed_state_t)); + long secs, usecs; + + if ((feed->conf.format != SFX_PCM_FORMAT_S16_LE) && + (feed->conf.format != SFX_PCM_FORMAT_U8)) { + fprintf(stderr, "[dc-mixer] Unsupported feed format\n"); + feed->destroy(feed); + return; + } + + state->handle = snd_stream_alloc(callback, BUF_SIZE); + + if (state->handle == SND_STREAM_INVALID) { + fprintf(stderr, "[dc-mixer] Failed to allocate stream handle\n"); + feed->destroy(feed); + return; + } + + feed->frame_size = SFX_PCM_FRAME_SIZE(feed->conf); + state->mode = FEED_MODE_ALIVE; + state->feed = feed; + state->gap = 0; + + TAILQ_INSERT_TAIL(&feeds, state, entry); + + sci_gettime(&secs, &usecs); + state->time = sfx_new_timestamp(secs, usecs, feed->conf.rate); + snd_stream_start(state->handle, feed->conf.rate, + feed->conf.stereo != SFX_PCM_MONO); +} + +static void +mix_exit(sfx_pcm_mixer_t *self) +{ + snd_stream_shutdown(); +} + +static int +mix_process(sfx_pcm_mixer_t *self) +{ + feed_state_t *state, *state_next; + + TAILQ_FOREACH(state, &feeds, entry) { + snd_stream_poll(state->handle); + } + + state = TAILQ_FIRST(&feeds); + while (state) { + state_next = TAILQ_NEXT(state, entry); + if (state->mode == FEED_MODE_DEAD) { + snd_stream_stop(state->handle); + snd_stream_destroy(state->handle); + state->feed->destroy(state->feed); + TAILQ_REMOVE(&feeds, state, entry); + } + else if (state->mode == FEED_MODE_RESTART) { + snd_stream_stop(state->handle); + snd_stream_start(state->handle, state->feed->conf.rate, + state->feed->conf.stereo != SFX_PCM_MONO); + state->mode = FEED_MODE_ALIVE; + } + state = state_next; + } + + return SFX_OK; +} + +static void +mix_pause(sfx_pcm_mixer_t *self) +{ +} + +static void +mix_resume(sfx_pcm_mixer_t *self) +{ +} + +static int +pcm_init(sfx_pcm_device_t *self) +{ + return SFX_OK; +} + +static void +pcm_exit(sfx_pcm_device_t *self) +{ +} + +sfx_pcm_device_t sfx_pcm_driver_dc = { + "dc", + "0.1", + + pcm_init, + pcm_exit, + NULL, + NULL, + NULL, + + {0, 0, 0}, + 0, + NULL, + NULL +}; + +sfx_pcm_mixer_t sfx_pcm_mixer_dc = { + "dc", + "0.1", + + mix_init, + mix_exit, + mix_subscribe, + mix_pause, + mix_resume, + mix_process, + + 0, + 0, + NULL, + NULL, + NULL +}; diff --git a/engines/sci/sfx/mixer/mixers.c b/engines/sci/sfx/mixer/mixers.c new file mode 100644 index 0000000000..1c7e27ab17 --- /dev/null +++ b/engines/sci/sfx/mixer/mixers.c @@ -0,0 +1,55 @@ +/*************************************************************************** + mixers.c Copyright (C) 2003 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include "../mixer.h" +#include + +extern sfx_pcm_mixer_t sfx_pcm_mixer_soft_linear; + +#ifdef _DREAMCAST +extern sfx_pcm_mixer_t sfx_pcm_mixer_dc; +#endif + +static sfx_pcm_mixer_t *mixers[] = { +#ifdef _DREAMCAST + &sfx_pcm_mixer_dc, +#endif + &sfx_pcm_mixer_soft_linear, + NULL +}; + +sfx_pcm_mixer_t * +sfx_pcm_find_mixer(char *name) +{ + int i = 0; + + if (name) + while (mixers[i] && strcmp(name, mixers[i]->name)) + ++i; + + return mixers[i]; +} diff --git a/engines/sci/sfx/mixer/soft.c b/engines/sci/sfx/mixer/soft.c new file mode 100644 index 0000000000..9f833017df --- /dev/null +++ b/engines/sci/sfx/mixer/soft.c @@ -0,0 +1,988 @@ +/*************************************************************************** + mixer.c Copyright (C) 2003 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include "../mixer.h" +#include + +/* Max. number of microseconds in difference allowed between independent audio streams */ +#define TIMESTAMP_MAX_ALLOWED_DELTA 2000 + +/*#define DEBUG 3*/ +/* Set DEBUG to one of the following: +** anything -- high-level debugging (feed subscriptions/deletions etc.) +** >= 1 -- rough input and output analysis (once per call) +** >= 2 -- more detailed input analysis (once per call and feed) +** >= 3 -- fully detailed input and output analysis (once per frame and feed) +*/ + +/*#define DEBUG 0*/ + +#define MIN_DELTA_OBSERVATIONS 100 /* Number of times the mixer is called before it starts trying to improve latency */ +#define MAX_DELTA_OBSERVATIONS 1000000 /* Number of times the mixer is called before we assume we truly understand timing */ + +static int diagnosed_too_slow = 0; + +static volatile int mixer_lock = 0; + +/*#define DEBUG_LOCKS*/ +#ifdef DEBUG_LOCKS +# define DEBUG_ACQUIRE fprintf(stderr, "[ -LOCK -] ACKQ %d: %d\n", __LINE__, mixer_lock) +# define DEBUG_WAIT fprintf(stderr, "[ -LOCK -] WAIT %d: %d\n", __LINE__, mixer_lock); +# define DEBUG_RELEASE ; fprintf(stderr, "[ -LOCK -] REL %d: %d\n", __LINE__, mixer_lock); +#else +# define DEBUG_ACQUIRE +# define DEBUG_WAIT +# define DEBUG_RELEASE +#endif + +#define ACQUIRE_LOCK() ++mixer_lock; while (mixer_lock != 1) { DEBUG_WAIT sci_sched_yield(); } DEBUG_ACQUIRE +#define RELEASE_LOCK() --mixer_lock DEBUG_RELEASE + +struct mixer_private { + byte *outbuf; /* Output buffer to write to the PCM device next time */ + sfx_timestamp_t outbuf_timestamp; /* Timestamp associated with the output buffer */ + int have_outbuf_timestamp; /* Whether we really _have_ an associated timestamp */ + byte *writebuf; /* Buffer we're supposed to write to */ + gint32 *compbuf_l, *compbuf_r; /* Intermediate buffers for computation */ + int lastbuf_len; /* Number of frames stored in the last buffer */ + + long skew; /* Millisecond relative to which we compute time. This is the millisecond + ** part of the first time we emitted sound, to simplify some computations. */ + long lsec; /* Last point in time we updated buffers, if any (seconds since the epoch) */ + int played_this_second; /* Number of frames emitted so far in second lsec */ + + int max_delta; /* maximum observed time delta (using 'frames' as a metric unit) */ + int delta_observations; /* Number of times we played; confidence measure for max_delta */ + + /* Pause data */ + int paused; +}; + +#define P ((struct mixer_private *)(self->private_bits)) + + +static int +mix_init(sfx_pcm_mixer_t *self, sfx_pcm_device_t *device) +{ + self->dev = device; + self->private_bits /* = P */ = sci_malloc(sizeof(struct mixer_private)); + P->outbuf = P->writebuf = NULL; + P->lastbuf_len = 0; + P->compbuf_l = (gint32*)sci_malloc(sizeof(gint32) * device->buf_size); + P->compbuf_r = (gint32*)sci_malloc(sizeof(gint32) * device->buf_size); + P->played_this_second = 0; + P->paused = 0; +#ifdef DEBUG + sciprintf("[soft-mixer] Initialised device %s v%s (%d Hz, %d/%x)\n", + device->name, device->version, + device->conf.rate, device->conf.stereo, device->conf.format); +#endif + return SFX_OK; +} + +static inline unsigned int +gcd(unsigned int a, unsigned int b) +{ + if (a == b) + return a; + + if (a < b) { + unsigned int c = b % a; + + if (!c) + return a; + + return gcd(c, a); + } else + return gcd(b, a); +} + +static sfx_pcm_urat_t +urat(unsigned int nom, unsigned int denom) +{ + sfx_pcm_urat_t rv; + unsigned int g; + + rv.val = nom / denom; + nom -= rv.val * denom; + if (nom == 0) + g = 1; + else + g = gcd(nom, denom); + + rv.nom = nom / g; + rv.den = denom / g; + + return rv; +} + +static void +mix_subscribe(sfx_pcm_mixer_t *self, sfx_pcm_feed_t *feed) +{ + sfx_pcm_feed_state_t *fs; + ACQUIRE_LOCK(); + if (!self->feeds) { + self->feeds_allocd = 2; + self->feeds = (sfx_pcm_feed_state_t*)sci_malloc(sizeof(sfx_pcm_feed_state_t) + * self->feeds_allocd); + } else if (self->feeds_allocd == self->feeds_nr) { + self->feeds_allocd += 2; + self->feeds = (sfx_pcm_feed_state_t*)sci_realloc(self->feeds, + sizeof(sfx_pcm_feed_state_t) + * self->feeds_allocd); + } + + fs = self->feeds + self->feeds_nr++; + fs->feed = feed; + + feed->frame_size = SFX_PCM_FRAME_SIZE(feed->conf); + + /* fs->buf_size = (self->dev->buf_size + * (feed->conf.rate + + self->dev->conf.rate - 1)) + / self->dev->conf.rate; + */ + /* For the sake of people without 64 bit CPUs: */ + fs->buf_size = 2 + /* Additional safety */ + (self->dev->buf_size * + (1 + (feed->conf.rate / self->dev->conf.rate))); +fprintf(stderr, " ---> %d/%d/%d/%d = %d\n", + self->dev->buf_size, + feed->conf.rate, + self->dev->conf.rate, + feed->frame_size, + fs->buf_size); + + fs->buf = (byte*)sci_malloc(fs->buf_size * feed->frame_size); +fprintf(stderr, " ---> --> %d for %p at %p\n", fs->buf_size * feed->frame_size, (void *)fs, (void *)fs->buf); +{int i; for (i = 0; i < fs->buf_size * feed->frame_size; i++) +fs->buf[i] = 0xa5; } + fs->scount = urat(0, 1); + fs->spd = urat(feed->conf.rate, self->dev->conf.rate); + fs->scount.den = fs->spd.den; + fs->ch_old.left = 0; + fs->ch_old.right = 0; + fs->ch_new.left = 0; + fs->ch_new.right = 0; + fs->mode = SFX_PCM_FEED_MODE_ALIVE; + + /* If the feed can't provide us with timestamps, we don't need to wait for it to do so */ + fs->pending_review = (feed->get_timestamp)? 1 : 0; + + fs->frame_bufstart = 0; + +#ifdef DEBUG + sciprintf("[soft-mixer] Subscribed %s-%x (%d Hz, %d/%x) at %d+%d/%d, buffer size %d\n", + feed->debug_name, feed->debug_nr, feed->conf.rate, feed->conf.stereo, feed->conf.format, + fs->spd.val, fs->spd.nom, fs->spd.den, fs->buf_size); +#endif + RELEASE_LOCK(); +} + + +static void +_mix_unsubscribe(sfx_pcm_mixer_t *self, sfx_pcm_feed_t *feed) +{ + int i; +#ifdef DEBUG + sciprintf("[soft-mixer] Unsubscribing %s-%x\n", feed->debug_name, feed->debug_nr); +#endif + for (i = 0; i < self->feeds_nr; i++) { + sfx_pcm_feed_state_t *fs = self->feeds + i; + + if (fs->feed == feed) { + feed->destroy(feed); + + if (fs->buf) + sci_free(fs->buf); + + self->feeds_nr--; + + /* Copy topmost into deleted so that we don't have any holes */ + if (i != self->feeds_nr) + self->feeds[i] = self->feeds[self->feeds_nr]; + + if (self->feeds_allocd > 8 && self->feeds_allocd > (self->feeds_nr << 1)) { + /* Limit memory waste */ + self->feeds_allocd >>= 1; + self->feeds + = (sfx_pcm_feed_state_t*)sci_realloc(self->feeds, + sizeof(sfx_pcm_feed_state_t) + * self->feeds_allocd); + } + + for (i = 0; i < self->feeds_nr; i++) + fprintf(stderr, " Feed #%d: %s-%x\n", + i, self->feeds[i].feed->debug_name, + self->feeds[i].feed->debug_nr); + + return; + } + } + + fprintf(stderr, "[sfx-mixer] Assertion failed: Deleting invalid feed %p out of %d\n", + (void *)feed, self->feeds_nr); + + BREAKPOINT(); +} + +static void +mix_unsubscribe(sfx_pcm_mixer_t *self, sfx_pcm_feed_t *feed) +{ + ACQUIRE_LOCK(); + _mix_unsubscribe(self, feed); + RELEASE_LOCK(); +} + + +static void +mix_exit(sfx_pcm_mixer_t *self) +{ + ACQUIRE_LOCK(); + while (self->feeds_nr) + _mix_unsubscribe(self, self->feeds[0].feed); + + if (P->outbuf) + sci_free(P->outbuf); + if (P->writebuf) + sci_free(P->writebuf); + + if (P->compbuf_l) + sci_free(P->compbuf_l); + if (P->compbuf_l) + sci_free(P->compbuf_r); + + sci_free(P); + self->private_bits /* = P */ = NULL; + RELEASE_LOCK(); + +#ifdef DEBUG + sciprintf("[soft-mixer] Uninitialising mixer\n"); +#endif +} + + +#define LIMIT_16_BITS(v) \ + if (v < -32767) \ + v = -32768; \ + else if (v > 32766) \ + v = 32767 + +static inline void +mix_compute_output(sfx_pcm_mixer_t *self, int outplen) +{ + int frame_i; + sfx_pcm_config_t conf = self->dev->conf; + int use_16 = conf.format & SFX_PCM_FORMAT_16; + int bias = conf.format & ~SFX_PCM_FORMAT_LMASK; + byte *lchan, *rchan = NULL; + /* Don't see how this could possibly wind up being + ** used w/o initialisation, but you never know... */ + gint32 *lsrc = P->compbuf_l; + gint32 *rsrc = P->compbuf_r; + int frame_size = SFX_PCM_FRAME_SIZE(conf); + + + if (!P->writebuf) + P->writebuf = (byte*)sci_malloc(self->dev->buf_size * frame_size + 4); + + if (conf.stereo) { + if (conf.stereo == SFX_PCM_STEREO_RL) { + lchan = P->writebuf + ((use_16)? 2 : 1); + rchan = P->writebuf; + } else { + lchan = P->writebuf; + rchan = P->writebuf + ((use_16)? 2 : 1); + } + } else + lchan = P->writebuf; + + + for (frame_i = 0; frame_i < outplen; frame_i++) { + int left = *lsrc++; + int right = *rsrc++; + + if (conf.stereo) { + LIMIT_16_BITS(left); + LIMIT_16_BITS(right); + + if (!use_16) { + left >>= 8; + right >>= 8; + } + + left += bias; + right += bias; + + if (use_16) { + if (SFX_PCM_FORMAT_LE == (conf.format & SFX_PCM_FORMAT_ENDIANNESS)) { + lchan[0] = left & 0xff; + lchan[1] = (left >> 8) & 0xff; + rchan[0] = right & 0xff; + rchan[1] = (right >> 8) & 0xff; + } else { + lchan[1] = left & 0xff; + lchan[0] = (left >> 8) & 0xff; + rchan[1] = right & 0xff; + rchan[0] = (right >> 8) & 0xff; + } + + lchan += 4; + rchan += 4; + } else { + *lchan = left & 0xff; + *rchan = right & 0xff; + + lchan += 2; + rchan += 2; + } + + } else { + left += right; + left >>= 1; + LIMIT_16_BITS(left); + if (!use_16) + left >>= 8; + + left += bias; + + if (use_16) { + if (SFX_PCM_FORMAT_LE == (conf.format & SFX_PCM_FORMAT_ENDIANNESS)) { + lchan[0] = left & 0xff; + lchan[1] = (left >> 8) & 0xff; + } else { + lchan[1] = left & 0xff; + lchan[0] = (left >> 8) & 0xff; + } + + lchan += 2; + } else { + *lchan = left & 0xff; + lchan += 1; + } + } + } +} + +static inline void +mix_swap_buffers(sfx_pcm_mixer_t *self) +{ /* Swap buffers */ + byte *tmp = P->outbuf; + P->outbuf = P->writebuf; + P->writebuf = tmp; +} + + +#define FRAME_OFFSET(usecs) \ + ((usecs >> 7) /* approximate, since uint32 is too small */ \ + * ((long) self->dev->conf.rate)) \ + / (1000000L >> 7) + +static inline int +mix_compute_buf_len(sfx_pcm_mixer_t *self, int *skip_frames) + /* Computes the number of frames we ought to write. It tries to minimise the number, + ** in order to reduce latency. */ + /* It sets 'skip_frames' to the number of frames to assume lost by latency, effectively + ** skipping them. */ +{ + int free_frames; + int played_frames = 0; /* since the last call */ + long secs, usecs; + int frame_pos; + int result_frames; + + sci_gettime(&secs, &usecs); + + if (!P->outbuf) { + /* Called for the first time ever? */ + P->skew = usecs; + P->lsec = secs; + P->max_delta = 0; + P->delta_observations = 0; + P->played_this_second = 0; + *skip_frames = 0; + return self->dev->buf_size; + } + + /* fprintf(stderr, "[%d:%d]S%d ", secs, usecs, P->skew);*/ + + if (P->skew > usecs) { + secs--; + usecs += (1000000 - P->skew); + } + else + usecs -= P->skew; + + frame_pos = FRAME_OFFSET(usecs); + + played_frames = frame_pos - P->played_this_second + + ((secs - P->lsec) * self->dev->conf.rate); + /* + fprintf(stderr, "%d:%d - %d:%d => %d\n", secs, frame_pos, + P->lsec, P->played_this_second, played_frames); + */ + + if (played_frames > self->dev->buf_size) + played_frames = self->dev->buf_size; + + /* + fprintf(stderr, "Between %d:? offset=%d and %d:%d offset=%d: Played %d at %d\n", P->lsec, P->played_this_second, + secs, usecs, frame_pos, played_frames, self->dev->conf.rate); + */ + + + if (played_frames > P->max_delta) + P->max_delta = played_frames; + + free_frames = played_frames; + + if (free_frames > self->dev->buf_size) { + if (!diagnosed_too_slow) { + sciprintf("[sfx-mixer] Your timer is too slow for your PCM output device (%d/%d), free=%d.\n" + "[sfx-mixer] You might want to try changing the device, timer, or mixer, if possible.\n", + played_frames, self->dev->buf_size, free_frames); + } + diagnosed_too_slow = 1; + + *skip_frames = free_frames - self->dev->buf_size; + free_frames = self->dev->buf_size; + } else + *skip_frames = 0; + + ++P->delta_observations; + if (P->delta_observations > MAX_DELTA_OBSERVATIONS) + P->delta_observations = MAX_DELTA_OBSERVATIONS; + +/* /\* Disabled, broken *\/ */ +/* if (0 && P->delta_observations > MIN_DELTA_OBSERVATIONS) { /\* Start improving after a while *\/ */ +/* int diff = self->dev->conf.rate - P->max_delta; */ + +/* /\* log-approximate P->max_delta over time *\/ */ +/* recommended_frames = P->max_delta + */ +/* ((diff * MIN_DELTA_OBSERVATIONS) / P->delta_observations); */ +/* /\* WTF? *\/ */ +/* } else */ +/* recommended_frames = self->dev->buf_size; /\* Initially, keep the buffer full *\/ */ + +#if (DEBUG >= 1) + sciprintf("[soft-mixer] played since last time: %d, recommended: %d, free: %d\n", + played_frames, recommended_frames, free_frames); +#endif + + result_frames = free_frames; + + if (result_frames < 0) + result_frames = 0; + + P->played_this_second += result_frames; + while (P->played_this_second >= self->dev->conf.rate) { + /* Won't normally happen more than once */ + P->played_this_second -= self->dev->conf.rate; + P->lsec++; + } + + if (result_frames > self->dev->buf_size) { + fprintf(stderr, "[soft-mixer] Internal assertion failed: frames-to-write %d > %d\n", + result_frames, self->dev->buf_size); + } + return result_frames; +} + + + +#define READ_NEW_VALUES() \ + if (frames_left > 0) { \ + if (bias) { /* unsigned data */ \ + if (!use_16) { \ + c_new.left = (*lsrc) << 8; \ + c_new.right = (*rsrc) << 8; \ + } else { \ + if (conf.format & SFX_PCM_FORMAT_LE) { \ + c_new.left = lsrc[0] | lsrc[1] << 8; \ + c_new.right = rsrc[0] | rsrc[1] << 8; \ + } else { \ + c_new.left = lsrc[1] | lsrc[0] << 8; \ + c_new.right = rsrc[1] | rsrc[0] << 8; \ + } \ + } \ + } else { /* signed data */ \ + if (!use_16) { \ + c_new.left = (*((signed char *)lsrc)) << 8; \ + c_new.right = (*((signed char *)rsrc)) << 8; \ + } else { \ + if (conf.format & SFX_PCM_FORMAT_LE) { \ + c_new.left = lsrc[0] | ((signed char *)lsrc)[1] << 8; \ + c_new.right = rsrc[0] | ((signed char *)rsrc)[1] << 8; \ + } else { \ + c_new.left = lsrc[1] | ((signed char *)lsrc)[0] << 8; \ + c_new.right = rsrc[1] | ((signed char *)rsrc)[0] << 8; \ + } \ + } \ + } \ + \ + c_new.left -= bias; \ + c_new.right -= bias; \ + \ + lsrc += frame_size; \ + rsrc += frame_size; \ + } else { \ + c_new.left = c_new.right = 0; \ + break; \ + } + + +static volatile int xx_offset; +static volatile int xx_size; + +static void +mix_compute_input_linear(sfx_pcm_mixer_t *self, int add_result, + int len, sfx_timestamp_t *ts, sfx_timestamp_t base_ts) + /* if add_result is non-zero, P->outbuf should be added to rather than overwritten. */ + /* base_ts is the timestamp for the first frame */ +{ + sfx_pcm_feed_state_t *fs = self->feeds + add_result; + sfx_pcm_feed_t *f = fs->feed; + sfx_pcm_config_t conf = f->conf; + int use_16 = conf.format & SFX_PCM_FORMAT_16; + gint32 *lchan = P->compbuf_l; + gint32 *rchan = P->compbuf_r; + int frame_size = f->frame_size; + byte *wr_dest = fs->buf + (frame_size * fs->frame_bufstart); + byte *lsrc = fs->buf; + byte *rsrc = fs->buf; + /* Location to write to */ + int frames_nr; + int bias = (conf.format & ~SFX_PCM_FORMAT_LMASK)? 0x8000 : 0; + /* We use this only on a 16 bit level here */ + + /* The two most extreme source frames we consider for a + ** destination frame */ + struct twochannel_data c_old = fs->ch_old; + struct twochannel_data c_new = fs->ch_new; + + int frames_read = 0; + int frames_left; + int write_offset; /* Iterator for translation */ + int delay_frames = 0; /* Number of frames (dest buffer) at the beginning we skip */ + + /* First, compute the number of frames we want to retreive */ + frames_nr = fs->spd.val * len; + /* A little complicated since we must consider partial frames */ + frames_nr += (fs->spd.nom * len + + (fs->scount.den - fs->scount.nom) /* remember that we may have leftovers */ + + (fs->spd.den - 1 /* round up */) + ) + / fs->spd.den; + + ts->secs = -1; + + if (frames_nr > fs->buf_size) { + fprintf(stderr, "%d (%d*%d + somethign) bytes, but only %d allowed!!!!!\n", + frames_nr * f->frame_size, + fs->spd.val, len, + fs->buf_size); + BREAKPOINT(); + } + + if (fs->pending_review) { + int newmode = PCM_FEED_EMPTY; /* empty unless a get_timestamp() tells otherwise */ + + RELEASE_LOCK(); + /* Retrieve timestamp */ + if (f->get_timestamp) + newmode = f->get_timestamp(f, ts); + ACQUIRE_LOCK(); + + fs = self->feeds + add_result; + /* Reset in case of status update */ + + switch (newmode) { + + case PCM_FEED_TIMESTAMP: { + /* Compute the number of frames the returned timestamp is in the future: */ + delay_frames = + sfx_timestamp_frame_diff(sfx_timestamp_renormalise(*ts, base_ts.frame_rate), + base_ts); + + if (delay_frames <= 0) + /* Start ASAP, even if it's too late */ + delay_frames = 0; + else + if (delay_frames > len) + delay_frames = len; + fs->pending_review = 0; + } + break; + + case PCM_FEED_EMPTY: + fs->mode = SFX_PCM_FEED_MODE_DEAD; + + /* ...fall through... */ + + case PCM_FEED_IDLE: + /* Clear audio buffer, if neccessary, and return */ + if (!add_result) { + memset(P->compbuf_l, 0, sizeof(gint32) * len); + memset(P->compbuf_r, 0, sizeof(gint32) * len); + } + return; + + default: + fprintf(stderr, "[soft-mixer] Fatal: Invalid mode returned by PCM feed %s-%d's get_timestamp(): %d\n", + f->debug_name, f->debug_nr, newmode); + exit(1); + } + } + + RELEASE_LOCK(); + /* Make sure we have sufficient information */ + if (frames_nr > delay_frames + fs->frame_bufstart) + frames_read = + f->poll(f, wr_dest, + frames_nr + - delay_frames + - fs->frame_bufstart); + + ACQUIRE_LOCK(); + fs = self->feeds + add_result; + + frames_read += fs->frame_bufstart; + frames_left = frames_read; + + /* Reset in case of status update */ + + /* Skip at the beginning: */ + if (delay_frames) { + if (!add_result) { + memset(lchan, 0, sizeof(gint32) * delay_frames); + memset(rchan, 0, sizeof(gint32) * delay_frames); + } + lchan += delay_frames; + rchan += delay_frames; + + len -= delay_frames; + } + + +#if (DEBUG >= 2) + sciprintf("[soft-mixer] Examining %s-%x (frame size %d); read %d/%d/%d, re-using %d frames\n", + f->debug_name, f->debug_nr, frame_size, frames_read, frames_nr, + fs->buf_size, fs->frame_bufstart); +#endif + + + if (conf.stereo == SFX_PCM_STEREO_LR) + rsrc += (use_16)? 2 : 1; + else if (conf.stereo == SFX_PCM_STEREO_RL) + lsrc += (use_16)? 2 : 1; + /* Otherwise, we let both point to the same place */ + +#if (DEBUG >= 2) + sciprintf("[soft-mixer] Stretching theoretical %d (physical %d) results to %d\n", frames_nr, frames_left, len); +#endif + for (write_offset = 0; write_offset < len; write_offset++) { + int leftsum = 0; /* Sum of any complete frames we used */ + int rightsum = 0; + + int left; /* Sum of the two most extreme source frames + ** we considered, i.e. the oldest and newest + ** one corresponding to the output frame we are + ** computing */ + int right; + + int frame_steps = fs->spd.val; + int j; + + if (fs->scount.nom >= fs->scount.den) { + fs->scount.nom -= fs->scount.den; /* Ensure fractional part < 1 */ + ++frame_steps; + } + if (frame_steps) + c_old = c_new; + +#if 0 + if (write_offset == 0) { + READ_NEW_VALUES(); + --frames_left; +#if (DEBUG >= 3) + sciprintf("[soft-mixer] Initial read %d:%d\n", c_new.left, c_new.right); +#endif + c_old = c_new; + } +#endif + + for (j = 0; j < frame_steps; j++) { + READ_NEW_VALUES(); + --frames_left; +#if (DEBUG >= 3) + sciprintf("[soft-mixer] Step %d/%d made %d:%d\n", j, frame_steps, c_new.left, c_new.right); +#endif + + /* The last frame will be subject to the fractional + ** part analysis, so we add it to 'left' and 'right' + ** later-- all others are added to (leftsum, rightsum). + */ + if (j+1 < frame_steps) { + leftsum += c_new.left; + rightsum += c_new.right; + } + } + + left = c_new.left * fs->scount.nom + + c_old.left * (fs->scount.den - fs->scount.nom); + right = c_new.right * fs->scount.nom + + c_old.right * (fs->scount.den - fs->scount.nom); + + /* Normalise */ + left /= fs->spd.den; + right /= fs->spd.den; + + + leftsum += left; + rightsum += right; + + + /* Make sure to divide by the number of frames we added here */ + if (frame_steps > 1) { + leftsum /= (frame_steps); + rightsum /= (frame_steps); + } + + +#if (DEBUG >= 3) + sciprintf("[soft-mixer] Ultimate result: %d:%d (frac %d:%d)\n", leftsum, rightsum, left, right); +#endif + + if (add_result) { + *(lchan++) += leftsum; + *(rchan++) += rightsum; + } else { + *(lchan++) = leftsum; + *(rchan++) = rightsum; + } + + fs->scount.nom += fs->spd.nom; /* Count up fractional part */ + } + + fs->ch_old = c_old; + fs->ch_new = c_new; + + /* If neccessary, zero out the rest */ + if (write_offset < len && !add_result) { + memset(lchan, 0, sizeof(gint32) * (len - write_offset)); + memset(rchan, 0, sizeof(gint32) * (len - write_offset)); + } + + /* Save whether we have a partial frame still stored */ + fs->frame_bufstart = frames_left; + + if (frames_left) { + xx_offset = ((frames_read - frames_left) * f->frame_size); + xx_size = frames_left * f->frame_size; + if (xx_offset + xx_size + >= fs->buf_size * f->frame_size) { + fprintf(stderr, "offset %d >= max %d!\n", + (xx_offset + xx_size), fs->buf_size * f->frame_size); + BREAKPOINT(); + } + + memmove(fs->buf, + fs->buf + ((frames_read - frames_left) * f->frame_size), + frames_left * f->frame_size); + } +#if (DEBUG >= 2) + sciprintf("[soft-mixer] Leaving %d over\n", fs->frame_bufstart); +#endif + + if (frames_read + delay_frames < frames_nr) { + if (f->get_timestamp) /* Can resume? */ + fs->pending_review = 1; + else + fs->mode = SFX_PCM_FEED_MODE_DEAD; /* Done. */ + } +} + +static int +mix_process_linear(sfx_pcm_mixer_t *self) +{ +ACQUIRE_LOCK(); +{ + int src_i; /* source feed index counter */ + int frames_skip; /* Number of frames to discard, rather than to emit */ + int buflen = mix_compute_buf_len(self, &frames_skip); /* Compute # of frames we must compute and write */ + int fake_buflen; + int timestamp_max_delta = 0; + int have_timestamp = 0; + sfx_timestamp_t start_timestamp; /* The timestamp at which the first frame will be played */ + sfx_timestamp_t min_timestamp; + sfx_timestamp_t timestamp; + + if (self->dev->get_output_timestamp) + start_timestamp = self->dev->get_output_timestamp(self->dev); + else { + long sec, usec; + sci_gettime(&sec, &usec); + start_timestamp = sfx_new_timestamp(sec, usec, self->dev->conf.rate); + } + + if ((P->outbuf) && (P->lastbuf_len)) { + sfx_timestamp_t ts; + int rv; + + if (P->have_outbuf_timestamp) { + ts = sfx_timestamp_renormalise(P->outbuf_timestamp, self->dev->conf.rate); + } + + rv = self->dev->output(self->dev, P->outbuf, + P->lastbuf_len, + (P->have_outbuf_timestamp)? &ts : NULL); + + if (rv == SFX_ERROR) { + RELEASE_LOCK(); + return rv; /* error */ + } + } + +#if (DEBUG >= 1) + if (self->feeds_nr) + sciprintf("[soft-mixer] Mixing %d output frames on %d input feeds\n", buflen, self->feeds_nr); +#endif + if (self->feeds_nr && !P->paused) { + /* Below, we read out all feeds in case we have to skip frames first, then get the + ** most current sound. 'fake_buflen' is either the actual buflen (for the last iteration) + ** or a fraction of the buf length to discard. */ + do { + if (frames_skip) { + if (frames_skip > self->dev->buf_size) + fake_buflen = self->dev->buf_size; + else + fake_buflen = frames_skip; + + frames_skip -= fake_buflen; + } else { + fake_buflen = buflen; + frames_skip = -1; /* Mark us as being completely done */ + } + + for (src_i = 0; src_i < self->feeds_nr; src_i++) { + mix_compute_input_linear(self, src_i, + fake_buflen, ×tamp, + start_timestamp); + + if (timestamp.secs >= 0) { + if (have_timestamp) { + int diff = sfx_timestamp_usecs_diff(min_timestamp, timestamp); + if (diff > 0) { + /* New earlier timestamp */ + timestamp = min_timestamp; + timestamp_max_delta += diff; + } else if (diff > timestamp_max_delta) + timestamp_max_delta = diff; + /* New max delta for timestamp */ + } else { + min_timestamp = timestamp; + have_timestamp = 1; + } + } + } + /* Destroy all feeds we finished */ + for (src_i = 0; src_i < self->feeds_nr; src_i++) + if (self->feeds[src_i].mode == SFX_PCM_FEED_MODE_DEAD) + _mix_unsubscribe(self, self->feeds[src_i].feed); + } while (frames_skip >= 0); + + } else { /* Zero it out */ + memset(P->compbuf_l, 0, sizeof(gint32) * buflen); + memset(P->compbuf_r, 0, sizeof(gint32) * buflen); + } + +#if (DEBUG >= 1) + if (self->feeds_nr) + sciprintf("[soft-mixer] Done mixing for this session, the result will be our next output buffer\n"); +#endif + +#if (DEBUG >= 3) + if (self->feeds_nr) { + int i; + sciprintf("[soft-mixer] Intermediate representation:\n"); + for (i = 0; i < buflen; i++) + sciprintf("[soft-mixer] Offset %d:\t[%04x:%04x]\t%d:%d\n", i, + P->compbuf_l[i] & 0xffff, P->compbuf_r[i] & 0xffff, + P->compbuf_l[i], P->compbuf_r[i]); + } +#endif + + if (timestamp_max_delta > TIMESTAMP_MAX_ALLOWED_DELTA) + sciprintf("[soft-mixer] Warning: Difference in timestamps between audio feeds is %d us\n", timestamp_max_delta); + + mix_compute_output(self, buflen); + P->lastbuf_len = buflen; + + /* Finalize */ + mix_swap_buffers(self); + if (have_timestamp) + P->outbuf_timestamp = sfx_timestamp_add(min_timestamp, + timestamp_max_delta >> 1); + P->have_outbuf_timestamp = have_timestamp; + +} RELEASE_LOCK(); + return SFX_OK; +} + +static void +mix_pause(sfx_pcm_mixer_t *self) +{ + ACQUIRE_LOCK(); + P->paused = 1; + RELEASE_LOCK(); +} + +static void +mix_resume(sfx_pcm_mixer_t *self) +{ + ACQUIRE_LOCK(); + P->paused = 0; + RELEASE_LOCK(); +} + +sfx_pcm_mixer_t sfx_pcm_mixer_soft_linear = { + "soft-linear", + "0.1", + + mix_init, + mix_exit, + mix_subscribe, + mix_pause, + mix_resume, + mix_process_linear, + + 0, + 0, + NULL, + NULL, + NULL +}; diff --git a/engines/sci/sfx/mixer/test.c b/engines/sci/sfx/mixer/test.c new file mode 100644 index 0000000000..20b3e952e1 --- /dev/null +++ b/engines/sci/sfx/mixer/test.c @@ -0,0 +1,351 @@ +/*************************************************************************** + test.c Copyright (C) 2003 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Mixer inspection/test program */ + + +#include "../mixer.h" +#include + +#if 0 +sfx_pcm_mixer_t *mix; + +int dev_init(sfx_pcm_device_t *self); +void dev_exit(sfx_pcm_device_t *self); +int dev_option(sfx_pcm_device_t *self, char *name, char *value); +int dev_output(sfx_pcm_device_t *self, byte *buf, int count); + +#define MIN_OUTPUT 128 +/* Min amount of output to compute */ + +#define DEVICES_NR 10 + +sfx_pcm_device_t devices[DEVICES_NR] = { + { "test-1", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_U8 }, 1024, NULL }, +#if (DEVICES_NR > 1) + { "test-2", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_STEREO_LR, SFX_PCM_FORMAT_U8 }, 1024, NULL }, + { "test-3", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_STEREO_RL, SFX_PCM_FORMAT_U8 }, 1024, NULL }, + { "test-4", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_S8 }, 1024, NULL }, + { "test-5", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_U16_LE }, 1024, NULL }, + { "test-6", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_U16_BE }, 1024, NULL }, + { "test-7", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_S16_LE }, 1024, NULL }, + { "test-8", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_S16_BE }, 1024, NULL }, + { "test-9", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_STEREO_RL, SFX_PCM_FORMAT_S16_LE }, 1024, NULL }, + { "test-10", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_STEREO_LR, SFX_PCM_FORMAT_U16_BE }, 1024, NULL } +#endif +}; + +int output_count; + +int dev_init(sfx_pcm_device_t *self) +{ + output_count = 0; + + fprintf(stderr, "[DEV] Initialised device %p as follows:\n" + "\trate = %d\n" + "\tstereo = %s\n" + "\tbias = %x\n" + "\tbytes/sample = %d\n" + "\tendianness = %s\n", + self, + self->conf.rate, + self->conf.stereo? ((self->conf.stereo == SFX_PCM_STEREO_LR)? "Left, Right" : "Right, Left") : "No", + self->conf.format & ~SFX_PCM_FORMAT_LMASK, + (self->conf.format & SFX_PCM_FORMAT_16)? 2 : 1, + ((self->conf.format & SFX_PCM_FORMAT_ENDIANNESS) == SFX_PCM_FORMAT_BE)? "big" : "little"); + return 0; +} + +void dev_exit(sfx_pcm_device_t *self) +{ + fprintf(stderr, "[DEV] Uninitialising device\n"); +} + +int dev_option(sfx_pcm_device_t *self, char *name, char *value) +{ + fprintf(stderr, "[DEV] Set option '%s' to '%s'\n", name, value); + return 0; +} + +int dev_output_enabled = 0; + +int dev_output(sfx_pcm_device_t *self, byte *buf, int count) +{ + int mono_sample_size = ((self->conf.format & SFX_PCM_FORMAT_16)? 2 : 1); + int sample_size = (self->conf.stereo? 2 : 1) * mono_sample_size; + int bias = self->conf.format & ~SFX_PCM_FORMAT_LMASK; + int is_bigendian = (self->conf.format & SFX_PCM_FORMAT_ENDIANNESS) == SFX_PCM_FORMAT_BE; + byte *left_channel = buf; + byte *right_channel = buf; + + if (!dev_output_enabled) + return 0; + + if (self->conf.format & SFX_PCM_FORMAT_16) + bias <<= 8; + + if (self->conf.stereo == SFX_PCM_STEREO_LR) + right_channel += mono_sample_size; + if (self->conf.stereo == SFX_PCM_STEREO_RL) + left_channel += mono_sample_size; + + while (count--) { + int right = right_channel[0]; + int left = left_channel[0]; + int second_byte = ((self->conf.format & SFX_PCM_FORMAT_16)? 1 : 0); + + if (second_byte) { + + if (is_bigendian) { + left = left << 8 | left_channel[1]; + right = right << 8 | right_channel[1]; + } else { + left = left | left_channel[1] << 8; + right = right | right_channel[1] << 8; + } + } + + left -= bias; + right -= bias; + + if (!second_byte) { + left <<= 8; + right <<= 8; + } + + fprintf(stderr, "[DEV] %p play %04x:\t%04x %04x\n", self, output_count++, left & 0xffff, right & 0xffff); + + left_channel += sample_size; + right_channel += sample_size; + } + return 0; +} + +/* Feeds for debugging */ + +typedef struct { + int i; +} int_struct; + +int feed_poll(sfx_pcm_feed_t *self, byte *dest, int size); +void feed_destroy(sfx_pcm_feed_t *self); + +int_struct private_bits[10] = { + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0} +}; + + +typedef struct { + int start; + int samples_nr; + byte *data; +} sample_feed_t; + +#define FEEDS_NR 4 + +sfx_pcm_feed_t feeds[FEEDS_NR] = { + { feed_poll, feed_destroy, &(private_bits[0]), + { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_S8 }, "test-feed", 0, 0} +#if FEEDS_NR > 1 + ,{ feed_poll, feed_destroy, &(private_bits[1]), + { 400, SFX_PCM_MONO, SFX_PCM_FORMAT_U8 }, "test-feed", 1, 0} +#endif +#if FEEDS_NR > 2 + ,{ feed_poll, feed_destroy, &(private_bits[2]), + { 20, SFX_PCM_MONO, SFX_PCM_FORMAT_S16_LE }, "test-feed", 2, 0} +#endif +#if FEEDS_NR > 3 + ,{ feed_poll, feed_destroy, &(private_bits[3]), + { 150, SFX_PCM_STEREO_LR, SFX_PCM_FORMAT_S8 }, "test-feed", 3, 0} +#endif + /* + ,{ feed_poll, feed_destroy, &(private_bits[4]), + {}, "test-feed", 4, 0} + ,{ feed_poll, feed_destroy, &(private_bits[5]), + {}, "test-feed", 5, 0} + */ +}; + +byte feed_data_0[] = {0xfd, 0xfe, 0xff, 0, 1, 2, 3, 4, 5, 6}; +byte feed_data_1[] = {0x80, 0x90, 0xA0, 0xB0, 0xC0, + 0xD0, 0xD0, 0xC0, 0xB0, 0xA0, 0x90, 0x80}; +byte feed_data_2[] = {0x00, 0x00, + 0x00, 0x80, + 0xe8, 0x03}; +byte feed_data_3[] = {0x00, 0x10, + 0x01, 0x20, + 0x02, 0x30}; + +sample_feed_t sample_feeds[FEEDS_NR] = { + { 1, 10, feed_data_0 } +#if FEEDS_NR > 1 + ,{ 21, 12, feed_data_1 } +#endif +#if FEEDS_NR > 2 + ,{ 0, 3, feed_data_2 } +#endif +#if FEEDS_NR > 3 + ,{ 40, 3, feed_data_3 } +#endif +}; + +void +feed_destroy(sfx_pcm_feed_t *self) +{ + int_struct *s = (int_struct *) self->internal; + s->i = 0; /* reset */ +} + + +int +feed_poll(sfx_pcm_feed_t *self, byte *dest, int size) +{ + int_struct *s = (int_struct *) self->internal; + int sample_size = self->sample_size; + sample_feed_t *data = &(sample_feeds[self->debug_nr]); + int bias = self->conf.format & ~SFX_PCM_FORMAT_LMASK; + byte neutral[4] = {0, 0, 0, 0}; + int i; +fprintf(stderr, "[feed] Asked for %d at %p, ss=%d\n", size, dest, sample_size); + if (bias) { + byte first = bias >> 8; + byte second = bias & 0xff; + + if ((self->conf.format & SFX_PCM_FORMAT_ENDIANNESS) == SFX_PCM_FORMAT_LE) { + int t = first; + first = second; + second = t; + } + + if (self->conf.format & SFX_PCM_FORMAT_16) { + neutral[0] = first; + neutral[1] = second; + neutral[2] = first; + neutral[3] = second; + } else { + neutral[0] = bias; + neutral[1] = bias; + } + } + + for (i = 0; i < size; i++) { + int t = s->i - data->start; + + if (t >= data->samples_nr) + return i; + + if (t >= 0) + memcpy(dest, data->data + t * sample_size, sample_size); + else + memcpy(dest, neutral, sample_size); + + dest += sample_size; + s->i++; + } + return size; +} + + + + +extern FILE *con_file; + +#define DELAY usleep((rand() / (RAND_MAX / 250L))) + + +int +main(int argc, char **argv) +{ + int dev_nr; + + mix = sfx_pcm_find_mixer(NULL); + + if (!mix) { + fprintf(stderr, "Error: Could not find a mixer!\n"); + return 1; + } else { + fprintf(stderr, "Running %s, v%s\n", + mix->name, mix->version); + } + con_file = stderr; + + srand(time(NULL)); + + for (dev_nr = 0; dev_nr < DEVICES_NR; dev_nr++) { + sfx_pcm_device_t *dev = &(devices[dev_nr++]); + int j; + dev->init(dev); + mix->init(mix, dev); + + dev_output_enabled = 0; + /* Prime it to our timing */ + for (j = 0; j < 250; j++) { + DELAY; + mix->process(mix); + } + dev_output_enabled = 1; + + fprintf(stderr, "[test] Subscribing...\n"); + + for (j = 0; j < FEEDS_NR; j++) + mix->subscribe(mix, &(feeds[j])); + + fprintf(stderr, "[test] Subscribed %d feeds.\n", + FEEDS_NR); + + while (output_count < MIN_OUTPUT) { + DELAY; + mix->process(mix); + fprintf(stderr, "\n"); + } + + fprintf(stderr, "[test] Preparing finalisation\n"); + mix->exit(mix); + fprintf(stderr, "[test] Mixer uninitialised\n"); + } +} + +#else +int main() {} +#endif diff --git a/engines/sci/sfx/mt32_GM_mapping/Makefile b/engines/sci/sfx/mt32_GM_mapping/Makefile new file mode 100644 index 0000000000..2d40cb25b0 --- /dev/null +++ b/engines/sci/sfx/mt32_GM_mapping/Makefile @@ -0,0 +1,15 @@ +CC =gcc +CFLAGS =-O2 -Wall +objects =main.o + +mtgm: $(objects) + $(CC) -o mtgm $(objects) + +main.o: main.c + $(CC) $(CFLAGS) -c main.c + +.PHONY: clean distclean +clean: + rm -f mtgm $(objects) +distclean: + rm -f mtgm $(objects) *~ diff --git a/engines/sci/sfx/mt32_GM_mapping/README b/engines/sci/sfx/mt32_GM_mapping/README new file mode 100644 index 0000000000..8fa94ec4fd --- /dev/null +++ b/engines/sci/sfx/mt32_GM_mapping/README @@ -0,0 +1,2 @@ +The sourcecode in this directory is not intended to be included in FreeSCI. + diff --git a/engines/sci/sfx/mt32_GM_mapping/gm_patches.c b/engines/sci/sfx/mt32_GM_mapping/gm_patches.c new file mode 100644 index 0000000000..b959a87461 --- /dev/null +++ b/engines/sci/sfx/mt32_GM_mapping/gm_patches.c @@ -0,0 +1,198 @@ +/*************************************************************************** + gm_patches.c Copyright (C) 2000 Rickard Lind + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +char *GM_Patch[128] = { +/*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" }; + +char *GM_RhythmKey[47] = { +/*035*/ "Acoustic Bass Drum", +/*036*/ "Bass Drum 1", +/*037*/ "Side Stick", +/*038*/ "Acoustic Snare", +/*039*/ "Hand Clap", +/*040*/ "Electric Snare", +/*041*/ "Low Floor Tom", +/*042*/ "Closed Hi-Hat", +/*043*/ "High Floor Tom", +/*044*/ "Pedal Hi-Hat", +/*045*/ "Low Tom", +/*046*/ "Open Hi-Hat", +/*047*/ "Low-Mid Tom", +/*048*/ "Hi-Mid Tom", +/*049*/ "Crash Cymbal 1", +/*050*/ "High Tom", +/*051*/ "Ride Cymbal 1", +/*052*/ "Chinese Cymbal", +/*053*/ "Ride Bell", +/*054*/ "Tambourine", +/*055*/ "Splash Cymbal", +/*056*/ "Cowbell", +/*057*/ "Crash Cymbal 2", +/*058*/ "Vibraslap", +/*059*/ "Ride Cymbal 2", +/*060*/ "Hi Bongo", +/*061*/ "Low Bongo", +/*062*/ "Mute Hi Conga", +/*063*/ "Open Hi Conga", +/*064*/ "Low Conga", +/*065*/ "High Timbale", +/*066*/ "Low Timbale", +/*067*/ "High Agogo", +/*068*/ "Low Agogo", +/*069*/ "Cabasa", +/*070*/ "Maracas", +/*071*/ "Short Whistle", +/*072*/ "Long Whistle", +/*073*/ "Short Guiro", +/*074*/ "Long Guiro", +/*075*/ "Claves", +/*076*/ "Hi Wood Block", +/*077*/ "Low Wood Block", +/*078*/ "Mute Cuica", +/*079*/ "Open Cuica", +/*080*/ "Mute Triangle" +/*081*/ "Open Triangle" }; diff --git a/engines/sci/sfx/mt32_GM_mapping/lb2map.txt b/engines/sci/sfx/mt32_GM_mapping/lb2map.txt new file mode 100644 index 0000000000..89c60aa552 --- /dev/null +++ b/engines/sci/sfx/mt32_GM_mapping/lb2map.txt @@ -0,0 +1,118 @@ + ---------------------------------------------------------------------- + | Dagger of Amon Ra | Boop boop be doop | Wahoo! | +-------------------------------------------------------------------------- +| ## | MT-32 Timbre | KSh FTn BR | General MIDI Patch | KSh VolA V | +-------------------------------------------------------------------------- +| 04 | AcouPiano3 | 00 00 12 | Acoustic Grand Piano | 000 000 3 | +| 05 | Honkytonk | 00 00 12 | Honky-tonk Piano | 000 000 3 | +| 06 | m ClarinetMS | 00 00 12 | Clarinet | 000 -020 3 | +| 07 | Acou Bass1 | 00 00 12 | Acoustic Bass | 000 -015 3 | +| 08 | Trombone 2 | 00 00 12 | Tuba | 000 005 3 | +| 09 | m StrSect1MS | 00 00 12 | String Ensemble 1 | 000 020 3 | +| 10 | m Fantasy2MS | 00 00 12 | Pad 1 (new age) | 000 010 3 | +| 11 | Flute 2 | 00 00 12 | Flute | 000 -020 3 | +| 12 | Timpani | 00 00 12 | Timpani | 000 -015 1 | +| 13 | Trumpet 1 | 00 00 12 | Trumpet | 000 030 3 | +| 14 | m FrHorn1MS2 | 00 00 12 | French Horn | 000 000 3 | +| 15 | m Oboe MS | 00 00 12 | Oboe | 000 -020 3 | +| 16 | m Pizz MS | 00 00 12 | Pizzicato Strings | 000 000 3 | +| 17 | m CymSwellMS | 00 00 12 | Reverse Cymbal | 000 000 3 | +| 18 | Xylophone | 00 00 12 | Xylophone | 000 000 3 | +| 19 | Bassoon | 00 00 12 | Bassoon | 000 020 3 | +| 20 | Accordion | 00 00 12 | Accordion | 000 000 3 | +| 21 | m BanjoLB2 | 00 00 12 | Banjo | 000 -020 3 | +| 22 | Marimba | 00 00 12 | Marimba | 000 000 3 | +| 23 | m WarmPadStr | 00 00 12 | String Ensemble 2 | 000 015 3 | +| 24 | m BassPizzMS | 00 00 12 | Pizzicato Strings | -012 000 3 | +| 25 | m WoodBlox | 00 00 12 | Woodblock | 000 000 3 | +| 26 | Vibe 1 | 00 00 00 | Vibraphone | 000 -010 3 | +| 27 | Sax 4 | 00 00 12 | Tenor Sax | 000 000 3 | +| 28 | m Glock MS | 00 00 12 | Glockenspiel | 000 000 3 | +| 29 | Koto | 00 00 12 | Koto | 000 000 3 | +| 30 | m Taiko | 00 00 12 | Taiko Drum | 012 -010 1 | +| 31 | Guitar 1 | 00 00 12 | Acoustic Guitar (nylon) | 000 -035 3 | +| 32 | m Bell Tree | 00 00 12 | Glockenspiel | 000 000 3 | +| 33 | Sitar | 00 00 12 | Sitar | 000 -010 3 | +| 34 | Harp 1 | 00 00 12 | Orchestral Harp | 000 -025 3 | +| 45 | m Fantasy2MS | 00 00 12 | Tubular Bells | 012 010 3 | +| 46 | m Window | 00 00 12 | Reverse Cymbal | -048 000 3 | +| 47 | m Snare | 00 00 00 | Woodblock | 000 000 0 | +| 48 | m CracklesMS | 00 00 12 | Woodblock | 000 000 0 | +| 49 | m TireSqueal | 00 00 12 | | | +| 50 | m Gurgle | 00 00 12 | | | +| 51 | m Toilet | 00 00 12 | | | +| 52 | m hiss | 00 00 12 | | | +| 53 | m IceBreakMS | 00 00 12 | | | +| 54 | m DoorSlamMS | 00 00 12 | Woodblock | -012 000 0 | +| 55 | m CreakyDLL1 | 00 00 12 | | | +| 56 | m Armor MS | 00 00 12 | Agogo | 012 -020 0 | +| 57 | m RatSqueek | 00 00 12 | Guitar Fret Noise | 012 000 3 | +| 58 | m StoneDr MS | 00 00 12 | Reverse Cymbal | -048 000 3 | +| 59 | m NewSplatMS | 00 00 12 | Melodic Tom | 000 000 2 | +| 60 | m Splash MS | 00 00 12 | | | +| 61 | m Bubbles | 00 00 12 | | | +| 62 | m ChurchB MS | 00 00 12 | Tubular Bells | 000 000 3 | +| 63 | m Thud MS | 00 00 12 | Taiko Drum | -012 000 2 | +| 64 | m TYPIMG | 00 00 12 | | | +| 65 | m Lock MS | 00 00 12 | Woodblock | 000 000 0 | +| 66 | m Window | 00 00 12 | Reverse Cymbal | 000 000 3 | +| 67 | m CabEngine | 00 00 12 | Tenor Sax | -060 050 3 | +| 68 | m Ocean MS | 00 00 12 | | | +| 69 | m Wind MS | 00 00 12 | | | +| 70 | Telephone | 00 00 12 | Telephone Ring | 000 000 3 | +| 71 | Bird Tweet | 00 00 12 | Bird Tweet | 000 000 3 | +| 72 | m Explode MS | 00 00 12 | Gunshot | -012 -015 3 | +| 73 | m SwmpBackgr | 00 00 12 | | | +| 74 | m Toing | 00 00 12 | | | +| 75 | m Lone Wolf | 00 00 12 | | | +| 76 | Whistle 2 | 00 00 12 | Whistle | 000 000 3 | +| 77 | m seagulls | 00 00 12 | Bird Tweet | 000 000 3 | +| 78 | m Scrubin'MS | 00 00 12 | Reverse Cymbal | 000 000 3 | +| 79 | m SqurWaveMS | 00 00 12 | Lead 1 (square) | 000 000 3 | +| 80 | m InHale MS | 00 00 12 | Breath Noise | 000 025 3 | +| 81 | m Arena2 MS | 00 00 12 | Applause | 000 000 3 | +| 82 | m ArenaNoSus | 00 00 12 | Applause | 000 000 3 | +| 92 | AcouPiano1 | 00 00 02 | | | +| 93 | AcouPiano1 | 00 00 02 | | | +| 94 | AcouPiano1 | 00 00 02 | | | +| 95 | AcouPiano1 | 00 00 02 | | | +-------------------------------------------------------------------------- + | ## | MT-32 Timbre | OL PP | General MIDI Rhythm Key | + -------------------------------------------------------- + | 35 | r Acou BD | 100 07 | Acoustic Bass Drum | + | 36 | r Acou BD | 100 07 | Acoustic Bass Drum | + | 37 | r Rim Shot | 100 06 | Side Stick | + | 38 | r Acou SD | 100 07 | Electric Snare | + | 39 | r Hand Clap | 100 08 | Hand Clap | + | 40 | r Acou SD | 100 06 | Electric Snare | + | 41 | r AcouLowTom | 100 11 | Low Floor Tom | + | 42 | r Clsd HiHat | 100 06 | Closed Hi-Hat | + | 43 | r AcouLowTom | 100 11 | High Floor Tom | + | 44 | r OpenHiHat2 | 100 06 | Pedal Hi-Hat | + | 45 | r AcouMidTom | 100 08 | Low Tom | + | 46 | r OpenHiHat1 | 100 06 | Open Hi-Hat | + | 47 | r AcouMidTom | 100 08 | Low-Mid Tom | + | 48 | r Acou HiTom | 100 03 | Hi-Mid Tom | + | 49 | r Crash Cym | 100 06 | Crash Cymbal 1 | + | 50 | r Acou HiTom | 100 03 | High Tom | + | 51 | r Ride Cym | 100 08 | Ride Cymbal 1 | + | 52 | m CymSwellMS | 100 07 | | + | 54 | r Tambourine | 100 09 | Tambourine | + | 55 | m ChokeCrash | 100 07 | Crash Cymbal 2 | + | 56 | r Cowbell | 100 07 | Cowbell | + | 60 | r High Bongo | 100 02 | Hi Bongo | + | 61 | r Low Bongo | 100 04 | Low Bongo | + | 62 | r Mt HiConga | 100 08 | Mute Hi Conga | + | 63 | r High Conga | 100 09 | Open Hi Conga | + | 64 | r Low Conga | 100 10 | Low Conga | + | 65 | r Hi Timbale | 100 07 | High Timbale | + | 66 | r LowTimbale | 100 05 | Low Timbale | + | 67 | r High Agogo | 100 02 | High Agogo | + | 68 | r Low Agogo | 100 02 | Low Agogo | + | 69 | r Cabasa | 100 09 | Cabasa | + | 70 | r Maracas | 100 04 | Maracas | + | 71 | r SmbaWhis S | 100 09 | Short Whistle | + | 72 | r SmbaWhis L | 100 09 | Long Whistle | + | 73 | r Quijada | 100 00 | | + | 75 | r Claves | 100 12 | Claves | + -------------------------------------------------------- diff --git a/engines/sci/sfx/mt32_GM_mapping/main.c b/engines/sci/sfx/mt32_GM_mapping/main.c new file mode 100644 index 0000000000..1db2b584b4 --- /dev/null +++ b/engines/sci/sfx/mt32_GM_mapping/main.c @@ -0,0 +1,157 @@ +/*************************************************************************** + main.c Copyright (C) 2000 Rickard Lind + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "mt32_timbres.c" +#include "gm_patches.c" + +void analyze(unsigned char *patch001, unsigned int length001, + unsigned char *patch004, unsigned int length004); + +int main(int argc, char **argv) +{ + int fd1, fd2; + unsigned char *patch001; + unsigned char *patch004; + unsigned int length001, length004; + + if (argc < 2) + return -1; + + if ((fd1 = open(argv[1], O_RDONLY)) < 0) + return -1; + if ((fd2 = open(argv[2], O_RDONLY)) < 0) { + close(fd1); + return -1; + } + + patch001 = (unsigned char *)sci_malloc(65536); + length001 = read(fd1, patch001, 65536); + close(fd1); + + patch004 = (unsigned char *)sci_malloc(65536); + length004 = read(fd2, patch004, 65536); + close(fd2); + + if (patch001[0] == 0x89 && patch001[1] == 0x00) + if (patch004[0] == 0x89 && patch004[1] == 0x00) + analyze(patch001 + 2, length001 - 2, patch004 + 2, length004 - 2); + else + analyze(patch001 + 2, length001 - 2, patch004, length004); + else + if (patch004[0] == 0x89 && patch004[1] == 0x00) + analyze(patch001, length001, patch004 + 2, length004 - 2); + else + analyze(patch001, length001, patch004, length004); + + free(patch001); + free(patch004); + + return 0; +} + +void analyze(unsigned char *patch001, unsigned int length001, + unsigned char *patch004, unsigned int length004) +{ + int i; + unsigned char *mt32patch; + unsigned char *mt32rhythm; + + printf(" ----------------------------------------------------------------------\n"); + printf(" | %.20s | %.20s | %.20s |\n", patch001, patch001 + 20, patch001 + 40); + printf("--------------------------------------------------------------------------\n"); + printf("| ## | MT-32 Timbre | KSh FTn BR | General MIDI Patch | KSh VolA V |\n"); + printf("--------------------------------------------------------------------------\n"); + for (i = 0; i < 96; i++) { + if (i < 48) + mt32patch = patch001 + 107 + i * 8; + else + mt32patch = patch001 + 110 + i * 8 + patch001[491] * 246; + + if (!((mt32patch[0] == 0) && + (mt32patch[1] == 0) && + (mt32patch[2] == 0) && + (mt32patch[3] == 0) && + (mt32patch[4] == 0) && + (mt32patch[5] == 0) && + (mt32patch[6] == 0) && + (mt32patch[7] == 0))) { + printf("| %02i |", i); + if (mt32patch[0] < 2) + if (mt32patch[0] == 0) + printf(" %.10s", MT32_Timbre[mt32patch[1]]); + else + printf(" %.10s", MT32_Timbre[mt32patch[1] + 64]); + else if (mt32patch[0] == 2) + printf(" m %.10s", patch001 + 492 + mt32patch[1] * 246); + else if (mt32patch[0] == 3) + printf(" r %.10s", MT32_RhythmTimbre[mt32patch[1]]); + printf(" | % 03i % 03i %02i | ", + mt32patch[2] - 24, + mt32patch[3] - 50, + mt32patch[4]); + if (patch004[i] != 0xFF) { + printf("%-23s ", GM_Patch[patch004[i]]); + printf("| % 04i % 04i %i |", + *((signed char *)(patch004) + i + 128), + *((signed char *)(patch004) + i + 256), + patch004[i + 513]); + } else + printf(" | |"); + printf("\n"); + } + } + printf("--------------------------------------------------------------------------\n"); + printf(" | ## | MT-32 Timbre | OL PP | General MIDI Rhythm Key |\n"); + printf(" --------------------------------------------------------\n"); + for (i = 0; i < 64; i++) + { + mt32rhythm = patch001 + 880 + i * 4 + patch001[491] * 246; + if ((mt32rhythm[0] < 94) && + !((mt32rhythm[0] == 0) && + (mt32rhythm[1] == 0) && + (mt32rhythm[2] == 0) && + (mt32rhythm[3] == 0)) && + !((mt32rhythm[0] == 1) && + (mt32rhythm[1] == 1) && + (mt32rhythm[2] == 1) && + (mt32rhythm[3] == 1))) { + printf(" | %02i |", i + 24); + if (mt32rhythm[0] < 64) + printf(" m %.10s", patch001 + 492 + mt32rhythm[0] * 246); + else + printf(" r %.10s", MT32_RhythmTimbre[mt32rhythm[0] - 64]); + printf(" | %03i %02i | ", mt32rhythm[1], mt32rhythm[2]); + if (patch004[384 + i + 24] != 0xFF) + printf("%-23s |", GM_RhythmKey[patch004[384 + i + 24] - 35]); + else + printf(" |"); + printf("\n"); + } + } + printf(" --------------------------------------------------------\n"); + return; +} diff --git a/engines/sci/sfx/mt32_GM_mapping/mt32_timbres.c b/engines/sci/sfx/mt32_GM_mapping/mt32_timbres.c new file mode 100644 index 0000000000..beb5fa97e3 --- /dev/null +++ b/engines/sci/sfx/mt32_GM_mapping/mt32_timbres.c @@ -0,0 +1,181 @@ +/*************************************************************************** + mt32_timbres.c Copyright (C) 2000 Rickard Lind + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +char *MT32_Timbre[128] = { +/*000*/ "AcouPiano1", +/*001*/ "AcouPiano2", +/*002*/ "AcouPiano3", +/*003*/ "ElecPiano1", +/*004*/ "ElecPiano2", +/*005*/ "ElecPiano3", +/*006*/ "ElecPiano4", +/*007*/ "Honkytonk ", +/*008*/ "Elec Org 1", +/*009*/ "Elec Org 2", +/*010*/ "Elec Org 3", +/*011*/ "Elec Org 4", +/*012*/ "Pipe Org 1", +/*013*/ "Pipe Org 2", +/*014*/ "Pipe Org 3", +/*015*/ "Accordion ", +/*016*/ "Harpsi 1 ", +/*017*/ "Harpsi 2 ", +/*018*/ "Harpsi 3 ", +/*019*/ "Clavi 1 ", +/*020*/ "Clavi 2 ", +/*021*/ "Clavi 3 ", +/*022*/ "Celesta 1 ", +/*023*/ "Celesta 2 ", +/*024*/ "Syn Brass1", +/*025*/ "Syn Brass2", +/*026*/ "Syn Brass3", +/*027*/ "Syn Brass4", +/*028*/ "Syn Bass 1", +/*029*/ "Syn Bass 2", +/*030*/ "Syn Bass 3", +/*031*/ "Syn Bass 4", +/*032*/ "Fantasy ", +/*033*/ "Harmo Pan ", +/*034*/ "Chorale ", +/*035*/ "Glasses ", +/*036*/ "Soundtrack", +/*037*/ "Atmosphere", +/*038*/ "Warm Bell ", +/*039*/ "Funny Vox ", +/*040*/ "Echo Bell ", +/*041*/ "Ice Rain ", +/*042*/ "Oboe 2001 ", +/*043*/ "Echo Pan ", +/*044*/ "DoctorSolo", +/*045*/ "Schooldaze", +/*046*/ "BellSinger", +/*047*/ "SquareWave", +/*048*/ "Str Sect 1", +/*049*/ "Str Sect 2", +/*050*/ "Str Sect 3", +/*051*/ "Pizzicato ", +/*052*/ "Violin 1 ", +/*053*/ "Violin 2 ", +/*054*/ "Cello 1 ", +/*055*/ "Cello 2 ", +/*056*/ "Contrabass", +/*057*/ "Harp 1 ", +/*058*/ "Harp 2 ", +/*059*/ "Guitar 1 ", +/*060*/ "Guitar 2 ", +/*061*/ "Elec Gtr 1", +/*062*/ "Elec Gtr 2", +/*063*/ "Sitar ", +/*064*/ "Acou Bass1", +/*065*/ "Acou Bass2", +/*066*/ "Elec Bass1", +/*067*/ "Elec Bass2", +/*068*/ "Slap Bass1", +/*069*/ "Slap Bass2", +/*070*/ "Fretless 1", +/*071*/ "Fretless 2", +/*072*/ "Flute 1 ", +/*073*/ "Flute 2 ", +/*074*/ "Piccolo 1 ", +/*075*/ "Piccolo 2 ", +/*076*/ "Recorder ", +/*077*/ "Panpipes ", +/*078*/ "Sax 1 ", +/*079*/ "Sax 2 ", +/*080*/ "Sax 3 ", +/*081*/ "Sax 4 ", +/*082*/ "Clarinet 1", +/*083*/ "Clarinet 2", +/*084*/ "Oboe ", +/*085*/ "Engl Horn ", +/*086*/ "Bassoon ", +/*087*/ "Harmonica ", +/*088*/ "Trumpet 1 ", +/*089*/ "Trumpet 2 ", +/*090*/ "Trombone 1", +/*091*/ "Trombone 2", +/*092*/ "Fr Horn 1 ", +/*093*/ "Fr Horn 2 ", +/*094*/ "Tuba ", +/*095*/ "Brs Sect 1", +/*096*/ "Brs Sect 2", +/*097*/ "Vibe 1 ", +/*098*/ "Vibe 2 ", +/*099*/ "Syn Mallet", +/*100*/ "Wind Bell ", +/*101*/ "Glock ", +/*102*/ "Tube Bell ", +/*103*/ "Xylophone ", +/*104*/ "Marimba ", +/*105*/ "Koto ", +/*106*/ "Sho ", +/*107*/ "Shakuhachi", +/*108*/ "Whistle 1 ", +/*109*/ "Whistle 2 ", +/*110*/ "BottleBlow", +/*111*/ "BreathPipe", +/*112*/ "Timpani ", +/*113*/ "MelodicTom", +/*114*/ "Deep Snare", +/*115*/ "Elec Perc1", +/*116*/ "Elec Perc2", +/*117*/ "Taiko ", +/*118*/ "Taiko Rim ", +/*119*/ "Cymbal ", +/*120*/ "Castanets ", +/*121*/ "Triangle ", +/*122*/ "Orche Hit ", +/*123*/ "Telephone ", +/*124*/ "Bird Tweet", +/*125*/ "OneNoteJam", +/*126*/ "WaterBells", +/*127*/ "JungleTune" }; + +char *MT32_RhythmTimbre[30] = { +/*00*/ "Acou BD ", +/*01*/ "Acou SD ", +/*02*/ "Acou HiTom", +/*03*/ "AcouMidTom", +/*04*/ "AcouLowTom", +/*05*/ "Elec SD ", +/*06*/ "Clsd HiHat", +/*07*/ "OpenHiHat1", +/*08*/ "Crash Cym ", +/*09*/ "Ride Cym ", +/*10*/ "Rim Shot ", +/*11*/ "Hand Clap ", +/*12*/ "Cowbell ", +/*13*/ "Mt HiConga", +/*14*/ "High Conga", +/*15*/ "Low Conga ", +/*16*/ "Hi Timbale", +/*17*/ "LowTimbale", +/*18*/ "High Bongo", +/*19*/ "Low Bongo ", +/*20*/ "High Agogo", +/*21*/ "Low Agogo ", +/*22*/ "Tambourine", +/*23*/ "Claves ", +/*24*/ "Maracas ", +/*25*/ "SmbaWhis L", +/*26*/ "SmbaWhis S", +/*27*/ "Cabasa ", +/*28*/ "Quijada ", +/*29*/ "OpenHiHat2" }; diff --git a/engines/sci/sfx/mt32_GM_mapping/pq1map.txt b/engines/sci/sfx/mt32_GM_mapping/pq1map.txt new file mode 100644 index 0000000000..b2040b54f7 --- /dev/null +++ b/engines/sci/sfx/mt32_GM_mapping/pq1map.txt @@ -0,0 +1,135 @@ + ---------------------------------------------------------------------- + | ..THE DEATH ANGEL.. | POLICE QUEST I | | +-------------------------------------------------------------------------- +| ## | MT-32 Timbre | KSh FTn BR | General MIDI Patch | KSh VolA V | +-------------------------------------------------------------------------- +| 00 | m FEEDBAK AX | 00 00 12 | Distortion Guitar | -012 065 3 | +| 01 | m REV CYMBAL | 00 00 12 | Reverse Cymbal | 000 000 0 | +| 02 | m ANALOG SYN | 00 00 12 | Pad 3 (polysynth) | 000 127 3 | +| 03 | m STACKBASS | 00 00 12 | Slap Bass 2 | -012 100 3 | +| 04 | m ORGAN B | 00 00 12 | Drawbar Organ | 000 055 3 | +| 05 | Syn Mallet | 00 00 12 | Dulcimer | 000 000 0 | +| 06 | m HARD RIDE | 00 00 12 | | | +| 07 | Orche Hit | 00 00 12 | Orchestra Hit | 000 000 0 | +| 08 | m HEFTY BASS | 00 00 12 | Electric Bass (finger) | -024 100 3 | +| 09 | AcouPiano1 | 00 00 12 | Acoustic Grand Piano | 000 000 0 | +| 10 | m SpaceVibes | 00 00 12 | Vibraphone | 000 000 0 | +| 11 | m CIGARETTE | -24 00 12 | | | +| 12 | AcouPiano1 | 00 00 12 | Acoustic Grand Piano | 000 000 0 | +| 13 | Castanets | 00 00 12 | | | +| 14 | AcouPiano1 | 00 00 12 | Acoustic Grand Piano | 000 000 0 | +| 15 | m LUSH STRNG | 00 00 12 | String Ensemble 1 | 000 117 3 | +| 16 | Fantasy | 00 00 12 | Pad 1 (new age) | 000 000 0 | +| 17 | AcouPiano1 | 00 00 12 | Acoustic Grand Piano | 000 000 0 | +| 18 | m BIG BANJO | 00 00 12 | FX 3 (crystal) | 000 -010 3 | +| 19 | Soundtrack | 00 00 12 | FX 2 (soundtrack) | 000 000 0 | +| 20 | Timpani | 00 00 12 | Timpani | 000 000 0 | +| 21 | Fr Horn 1 | 00 00 12 | French Horn | 000 000 0 | +| 22 | Trumpet 2 | 00 00 12 | Trumpet | 000 000 3 | +| 23 | m SMOKING | 00 00 12 | | | +| 24 | m F VoxStrg | 00 00 12 | String Ensemble 1 | 000 000 0 | +| 25 | m SqurWaveMS | 00 00 12 | | | +| 26 | m TYPIMG | 00 00 12 | | | +| 27 | m SnglVox MS | 00 00 12 | | | +| 28 | Str Sect 2 | 00 00 12 | String Ensemble 1 | 000 000 0 | +| 29 | Guitar 2 | 00 00 12 | Electric Guitar (clean) | 000 000 0 | +| 30 | Elec Gtr 2 | 00 00 12 | Electric Guitar (clean) | 000 000 0 | +| 31 | Harmonica | 00 00 12 | Harmonica | 000 000 0 | +| 35 | Sax 3 | 00 00 12 | Alto Sax | 000 000 0 | +| 36 | Slap Bass1 | 00 00 12 | Slap Bass 1 | 000 000 0 | +| 37 | Fretless 1 | 00 00 12 | Fretless Bass | 000 000 0 | +| 38 | Acou Bass1 | 00 00 12 | Acoustic Bass | 000 000 0 | +| 41 | r Crash Cym | 00 00 12 | | | +| 42 | r Acou BD | 00 00 12 | | | +| 43 | m RAP SNARE | 00 00 12 | | | +| 44 | m BIG SNARE | 00 00 00 | | | +| 45 | m CLOSED HAT | 00 00 12 | | | +| 46 | m SLOSH HAT | 00 00 12 | | | +| 47 | m Gun MS | 00 00 12 | Gunshot | 000 000 0 | +| 48 | m IceBreakMS | 00 00 12 | | | +| 49 | m SHOWER | 00 00 12 | | | +| 50 | m Pft MS | 00 00 12 | | | +| 51 | m Chicago MS | 00 00 12 | Bright Acoustic Piano | 000 030 0 | +| 52 | m ROCK GUIT1 | 00 00 12 | Distortion Guitar | -012 127 3 | +| 53 | m Dog MS | 00 00 12 | | | +| 54 | m TriangleMS | 00 00 12 | | | +| 55 | m CLICKS | 00 00 12 | Woodblock | 000 000 0 | +| 56 | Bird Tweet | 00 00 12 | Bird Tweet | 000 000 0 | +| 57 | AcouPiano1 | 00 00 12 | Acoustic Grand Piano | 000 000 0 | +| 58 | m CARHORN3+4 | 00 00 12 | | | +| 59 | m CUFFS | 00 00 12 | | | +| 60 | m ELEC PHONE | 00 00 12 | | | +| 61 | m KongHit | 00 00 12 | | | +| 62 | m SCUFFLE | 00 00 12 | | | +| 63 | m FUNK PING | 00 00 12 | Orchestral Harp | 027 -010 0 | +| 64 | m BIG TOMS | 00 00 12 | Taiko Drum | 000 000 0 | +| 65 | m ElecGtr MS | 00 00 12 | Electric Guitar (clean) | -012 -005 3 | +| 66 | m Lock MS | 00 00 12 | | | +| 67 | Fretless 1 | 00 00 02 | Fretless Bass | 000 000 0 | +| 70 | m Armor MS | 00 00 12 | | | +| 71 | m ElecGtr MS | 00 00 02 | Electric Guitar (clean) | 000 000 0 | +| 77 | Telephone | 00 00 00 | Telephone Ring | 000 000 0 | +| 78 | m StoneDr MS | 00 00 12 | | | +| 79 | m Shuffle | 00 00 12 | | | +| 80 | m Buzzer | 00 00 12 | | | +| 81 | Vibe 1 | 00 00 12 | Vibraphone | 000 000 0 | +| 82 | m ElecGtr MS | 00 00 12 | Electric Guitar (clean) | 000 000 0 | +| 83 | r Hand Clap | 00 00 12 | | | +| 84 | m Drip MS | 00 00 12 | | | +| 85 | m CracklesMS | 00 00 12 | | | +| 86 | m TireSqueal | 00 00 12 | Bag pipe | 000 025 3 | +| 87 | m Explode MS | 00 00 12 | | | +| 88 | m DRAWER | 00 00 12 | | | +| 89 | m KABOOM | 00 00 12 | | | +| 90 | m GUN/STATIC | 00 00 12 | | | +| 91 | m TRAFFIC | 00 00 12 | Seashore | 000 000 0 | +| 92 | m DOOR SLAM | 00 00 12 | | | +| 93 | m CAR DOOR | 00 00 12 | | | +| 94 | m STEPS | 00 00 12 | | | +| 95 | m DIESEL | 00 00 12 | | | +-------------------------------------------------------------------------- + | ## | MT-32 Timbre | OL PP | General MIDI Rhythm Key | + -------------------------------------------------------- + | 34 | r Acou SD | 100 07 | Electric Snare | + | 35 | r Acou BD | 100 07 | Acoustic Bass Drum | + | 36 | m BIG KICK | 095 07 | Bass Drum 1 | + | 37 | r Rim Shot | 100 06 | Side Stick | + | 38 | m RAP SNARE | 100 07 | Electric Snare | + | 39 | m SLAPSTICK | 096 08 | Hand Clap | + | 40 | m BIG SNARE | 100 06 | Acoustic Snare | + | 41 | m ACOU TOMS | 080 07 | Low Floor Tom | + | 42 | m CLOSED HAT | 090 06 | Closed Hi-Hat | + | 43 | m ACOU TOMS | 080 11 | High Floor Tom | + | 44 | m SLOSH HAT | 082 06 | Pedal Hi-Hat | + | 45 | m ACOU TOMS | 085 13 | Low Tom | + | 46 | m CHOKE HAT | 072 06 | Open Hi-Hat | + | 47 | m ACOU TOMS | 080 08 | Low-Mid Tom | + | 48 | m ACOU TOMS | 069 06 | Hi-Mid Tom | + | 49 | r Crash Cym | 095 06 | Crash Cymbal 1 | + | 50 | m HARD RIDE | 100 08 | Ride Cymbal 2 | + | 51 | m RIDE CYM | 090 08 | Ride Cymbal 1 | + | 52 | m CRASH CYM | 077 11 | Crash Cymbal 1 | + | 53 | m HEFTY BASS | 082 04 | | + | 54 | r Tambourine | 100 09 | Tambourine | + | 56 | r Cowbell | 100 07 | Cowbell | + | 57 | m StoneDr MS | 100 07 | | + | 58 | r Tambourine | 100 09 | Tambourine | + | 60 | r High Bongo | 100 02 | Hi Bongo | + | 61 | r Low Bongo | 100 04 | Low Bongo | + | 62 | r Mt HiConga | 100 08 | Mute Hi Conga | + | 63 | r High Conga | 100 09 | Open Hi Conga | + | 64 | r Low Conga | 100 10 | Low Conga | + | 65 | r Hi Timbale | 100 07 | High Timbale | + | 66 | r LowTimbale | 100 05 | Low Timbale | + | 67 | r High Agogo | 100 02 | High Agogo | + | 68 | r Low Agogo | 100 02 | Low Agogo | + | 69 | r Cabasa | 100 09 | Cabasa | + | 70 | r Maracas | 100 04 | Maracas | + | 71 | r SmbaWhis S | 100 09 | Short Whistle | + | 72 | r SmbaWhis L | 100 09 | Long Whistle | + | 73 | r Quijada | 100 10 | Short Guiro | + | 75 | r Claves | 100 12 | Claves | + | 76 | m Stir | 100 07 | Long Guiro | + | 77 | m Hit | 100 07 | Acoustic Snare | + | 78 | m Brushcym | 090 07 | Ride Cymbal 1 | + -------------------------------------------------------- diff --git a/engines/sci/sfx/mt32_GM_mapping/qfg1map.txt b/engines/sci/sfx/mt32_GM_mapping/qfg1map.txt new file mode 100644 index 0000000000..c1816786b1 --- /dev/null +++ b/engines/sci/sfx/mt32_GM_mapping/qfg1map.txt @@ -0,0 +1,123 @@ + ---------------------------------------------------------------------- + | *QUEST FOR GLORY I* | So U Want 2 B A HERO | QUEST FOR GLORY I | +-------------------------------------------------------------------------- +| ## | MT-32 Timbre | KSh FTn BR | General MIDI Patch | KSh VolA V | +-------------------------------------------------------------------------- +| 03 | m Toms MS | 00 00 12 | Melodic Tom | 000 000 1 | +| 04 | Trumpet 2 | 00 00 12 | Trumpet | 000 022 2 | +| 05 | m T-Bone2 MS | 00 00 12 | Trombone | 000 065 2 | +| 06 | m FrHorn1 MS | 00 00 12 | French Horn | 000 040 3 | +| 07 | m StrSect1MS | 00 00 12 | String Ensemble 1 | 000 127 2 | +| 08 | Timpani | 00 00 12 | Timpani | 000 000 2 | +| 09 | Contrabass | 00 00 12 | Contrabass | 000 080 2 | +| 10 | m Glock MS | 00 00 12 | Glockenspiel | 012 080 3 | +| 11 | m Flute MS | 00 00 12 | Flute | 000 045 3 | +| 12 | Tuba | 00 00 12 | Tuba | 000 127 3 | +| 13 | Brs Sect 1 | 00 00 12 | Brass Section | 000 100 3 | +| 14 | Fr Horn 1 | 00 00 12 | French Horn | 000 040 3 | +| 15 | Pizzicato | 00 00 12 | Pizzicato Strings | 000 000 2 | +| 17 | Acou Bass1 | -12 00 00 | Acoustic Bass | 000 050 2 | +| 18 | Sax 2 | 00 00 00 | Alto Sax | 000 040 1 | +| 19 | m EnglHornMS | 00 00 12 | English Horn | 012 040 3 | +| 20 | Pipe Org 1 | 00 00 00 | Church Organ | 000 000 3 | +| 21 | Panpipes | 00 00 12 | Pan Flute | 000 020 1 | +| 22 | Clarinet 1 | 00 00 12 | Clarinet | 000 020 3 | +| 23 | m BassPizzMS | 00 00 12 | Orchestral Harp | -012 050 1 | +| 24 | m Conga MS | 00 00 12 | Taiko Drum | 000 000 1 | +| 25 | Sitar | 00 00 12 | Sitar | 000 080 3 | +| 26 | m Snare MS | 00 00 00 | Taiko Drum | 000 000 1 | +| 27 | m ClarinetMS | 00 00 12 | Clarinet | 000 020 3 | +| 28 | m Fantasy2MS | 00 00 12 | Pad 4 (choir) | 000 050 1 | +| 29 | Guitar 1 | 00 00 12 | Acoustic Guitar (nylon) | 000 000 1 | +| 30 | Chorale | 00 00 12 | Pad 4 (choir) | 000 020 2 | +| 31 | Syn Bass 1 | 00 00 12 | Synth Bass 1 | 000 020 1 | +| 32 | m GameSnd MS | 00 00 12 | Lead 1 (square) | 000 000 1 | +| 33 | m Calliope | 00 00 12 | Drawbar Organ | 000 000 1 | +| 34 | Whistle 1 | 00 00 12 | Whistle | 000 040 3 | +| 47 | m SKATE2 | 00 00 24 | | | +| 48 | m Claw MS | 00 00 12 | Synth Drum | 000 000 1 | +| 49 | m Flames MS | 00 00 12 | Breath Noise | 000 000 1 | +| 50 | m Swords MS | 00 00 12 | | | +| 51 | m Armor MS | 00 00 12 | | | +| 52 | m Splat MS | 00 00 00 | Synth Drum | 000 000 1 | +| 53 | m Fall MS | 00 00 00 | | | +| 54 | m Crash MS | 00 00 00 | Gunshot | 000 127 3 | +| 55 | m Laser MS | 00 00 00 | Lead 2 (sawtooth) | 000 -040 1 | +| 56 | m Vase MS | 00 00 00 | Gunshot | -012 000 1 | +| 57 | m Spit MS | 00 00 12 | Woodblock | -012 000 1 | +| 58 | m Bubbles | 00 00 12 | | | +| 59 | m Ninga MS | 00 00 12 | Breath Noise | 000 100 1 | +| 60 | m Flame2 MS | 00 00 12 | Breath Noise | 000 100 1 | +| 61 | m CracklesMS | 00 00 12 | Woodblock | 000 040 1 | +| 62 | m Gulp MS | 00 00 12 | | | +| 63 | m KnifeStkMS | 00 00 12 | Woodblock | 000 110 1 | +| 64 | m Pft MS | 00 00 12 | Helicopter | 000 110 1 | +| 65 | m Poof MS | 00 00 12 | | | +| 66 | Bird Tweet | 00 00 12 | Bird Tweet | 000 000 1 | +| 67 | m Owl MS | 00 00 12 | Bird Tweet | -012 000 1 | +| 68 | m Wind MS | 00 00 12 | | | +| 69 | m Cricket | 00 00 12 | Guitar Fret Noise | 000 000 1 | +| 70 | m SwmpBackgr | 00 00 12 | Guitar Fret Noise | 000 000 1 | +| 71 | m FireDartMS | 00 00 00 | Seashore | 000 120 1 | +| 72 | m LghtboltMS | 00 00 12 | Seashore | 000 120 1 | +| 73 | m Raspbry MS | 00 00 12 | Lead 2 (sawtooth) | 000 000 1 | +| 74 | m Explode MS | 00 00 13 | Helicopter | 000 000 1 | +| 75 | m Lock MS | 00 00 12 | Gunshot | 080 000 1 | +| 76 | m Buildup MS | 00 00 12 | | | +| 77 | m Boing MS | 00 00 24 | Taiko Drum | 000 000 1 | +| 78 | m Flames3 MS | 00 00 12 | Helicopter | 000 080 1 | +| 79 | m Thunder MS | 00 00 12 | Helicopter | -012 100 1 | +| 80 | m Meeps MS | 00 00 12 | SynthBrass 1 | 000 000 1 | +| 81 | m Bells MS | 00 00 12 | Tinkle Bell | 012 080 1 | +| 82 | m Skid MS | 00 00 12 | Helicopter | 000 000 1 | +| 83 | m RimShot MS | 00 00 12 | Woodblock | 000 000 1 | +| 84 | m WtrFall MS | 00 00 12 | | | +| 85 | m DoorSlamMS | 00 00 12 | Taiko Drum | 000 000 1 | +| 86 | m SmileFacMS | 00 00 12 | Helicopter | 000 040 1 | +| 87 | m Tumble MS | 00 00 12 | Synth Drum | 000 040 1 | +| 88 | m Punch MS | 00 00 12 | Synth Drum | 000 080 1 | +| 89 | m CreakyD MS | 00 00 12 | | | +| 90 | m CstlGateMS | 00 00 12 | Gunshot | -050 050 1 | +| 91 | m Hammer MS | 00 00 12 | | | +| 92 | m Horse1 MS | 00 00 12 | Woodblock | 000 000 1 | +| 93 | m Horse2 MS | 00 00 12 | Woodblock | 000 000 1 | +| 94 | m Kiss MS | 00 00 12 | Gunshot | 100 000 1 | +| 95 | m StrSect1MS | 00 00 12 | String Ensemble 1 | 000 127 2 | +-------------------------------------------------------------------------- + | ## | MT-32 Timbre | OL PP | General MIDI Rhythm Key | + -------------------------------------------------------- + | 36 | r Acou BD | 100 07 | Bass Drum 1 | + | 37 | r Rim Shot | 100 06 | Side Stick | + | 38 | r Acou SD | 100 07 | Acoustic Snare | + | 39 | r Hand Clap | 100 08 | Hand Clap | + | 40 | r Elec SD | 100 06 | Electric Snare | + | 41 | r AcouLowTom | 100 11 | Low Floor Tom | + | 42 | r Clsd HiHat | 100 06 | Closed Hi-Hat | + | 43 | r AcouLowTom | 100 11 | Low Floor Tom | + | 44 | r OpenHiHat2 | 100 06 | Open Hi-Hat | + | 45 | r AcouMidTom | 100 08 | Low-Mid Tom | + | 46 | r OpenHiHat1 | 100 06 | Open Hi-Hat | + | 47 | r AcouMidTom | 100 08 | Hi-Mid Tom | + | 48 | r Acou HiTom | 100 03 | High Tom | + | 49 | r Crash Cym | 100 06 | Crash Cymbal 1 | + | 50 | r Acou HiTom | 100 03 | High Tom | + | 51 | r Ride Cym | 100 08 | Ride Cymbal 1 | + | 52 | m CymSwellMS | 100 07 | | + | 54 | r Tambourine | 100 09 | Tambourine | + | 56 | r Cowbell | 100 07 | Cowbell | + | 60 | r High Bongo | 100 02 | Hi Bongo | + | 61 | r Low Bongo | 100 04 | Low Bongo | + | 62 | r Mt HiConga | 100 08 | Mute Hi Conga | + | 63 | r High Conga | 100 09 | Open Hi Conga | + | 64 | r Low Conga | 100 10 | Low Conga | + | 65 | r Hi Timbale | 100 07 | High Timbale | + | 66 | r LowTimbale | 100 05 | Low Timbale | + | 67 | r High Agogo | 100 02 | High Agogo | + | 68 | r Low Agogo | 100 02 | Low Agogo | + | 69 | r Cabasa | 100 09 | Cabasa | + | 70 | r Maracas | 100 04 | Maracas | + | 71 | r SmbaWhis S | 100 09 | Short Whistle | + | 72 | r SmbaWhis L | 100 09 | Long Whistle | + | 73 | r Quijada | 100 00 | Short Guiro | + | 75 | r Claves | 100 12 | Claves | + -------------------------------------------------------- diff --git a/engines/sci/sfx/old/Makefile b/engines/sci/sfx/old/Makefile new file mode 100644 index 0000000000..f7235d945b --- /dev/null +++ b/engines/sci/sfx/old/Makefile @@ -0,0 +1,24 @@ +CC = gcc +CFLAGS = -O2 -Wall +LIBS = -L/usr/lib -lasound +objects = main.o midi_mt32.o midiout.o midiout_unixraw.o midiout_alsaraw.o + +sciplaymidi: $(objects) + $(CC) $(LIBS) -o sciplaymidi $(objects) + +main.o: main.c + $(CC) $(CFLAGS) -c main.c +midi_mt32.o: midi_mt32.c midi_mt32.h midiout.h + $(CC) $(CFLAGS) -c midi_mt32.c +midiout.o: midiout.c midiout.h midiout_unixraw.h midiout_alsaraw.h + $(CC) $(CFLAGS) -c midiout.c +midiout_unixraw.o: midiout_unixraw.c midiout_unixraw.h + $(CC) $(CFLAGS) -c midiout_unixraw.c +midiout_alsaraw.o: midiout_alsaraw.c midiout_alsaraw.h + $(CC) $(CFLAGS) -c midiout_alsaraw.c + +.PHONY: clean distclean +clean: + rm -f sciplaymidi $(objects) +distclean: + rm -f sciplaymidi $(objects) *~ diff --git a/engines/sci/sfx/old/README b/engines/sci/sfx/old/README new file mode 100644 index 0000000000..c652175761 --- /dev/null +++ b/engines/sci/sfx/old/README @@ -0,0 +1,6 @@ +type 'make' and './sciplaymidi' to program your MT-32 with the file +'patch.001' in the current directory + +Only tested with Linux and ALSA... + +Rickard Lind diff --git a/engines/sci/sfx/old/ROADMAP b/engines/sci/sfx/old/ROADMAP new file mode 100644 index 0000000000..9a6ec1b4da --- /dev/null +++ b/engines/sci/sfx/old/ROADMAP @@ -0,0 +1,72 @@ +The way I would have made things if I had the time to do it +Rickard Lind , 2000-12-30 +------------------------------------------------------------------------------- + +Step 1: + +D rename "src/sound/midi.c" in freesci to "oldmidi.c" and still use it +D move my "midi*" and "midiout*" to "src/sound" +D change all "glib.h" to the real thing + +Step 2: + +D implement all note-playing, volume changing, reverb etc in "midi_mt32.*" + use disassembled sierra SCI1.1 driver as the main source of inspiration +D change "soundserver_null.c" to use the new device driver for MT-32 +* use "~/.freesci/config" to set ALSA/OSS and other options + +Step 3: + +* Implement a GM translation driver "midi_gm.*" using a parsed textfile + for instrument mapping +* Improve instrument mappings using new features such as keyshift, + volume adjust and velocity remap + +Step 4: + +* Reimplement a SCI0 soundserver using the new sound sub sytem +* PCM support (samples) with ALSA and possibly DirectX +* MPU-401 UART midiout driver for DOS/Windows + +Step 5: + +* SCI01, SCI1, SCI32 soundserver +* Adlib support "midi_opl2.*", "oplout_alsaraw.*", "oplout_pcmemu.*" +* PCM support Sound Blaster DOS + +Step 6: + +* Make it possible to play samples and use opl2 emulation (found in MAME + I think) at the same time through the same sound device + +Step 7: + +* All those other little nifty things... + +------------------------------------------------------------------------------- + +My idea concerning naming of the files: + +src/sound/midi.* wrapper for MIDI device drivers +src/sound/midiout.* wrapper for different methods to output + MIDI data +src/sound/pcm.* wrapper for PCM device drivers +src/sound/pcmout.* wrapper for different methods to output + PCM data +src/sound/midi_mt32.* Roland MT-32 device driver +src/sound/midi_opl2.* Adlib/OPL2 device driver +src/sound/midiout_alsaraw.* rawmidi output using alsalib +src/sound/midiout_unixraw.* rawmidi output using unix filesystem +src/sound/oplout_alsaraw.* opl output using alsa + +------------------------------------------------------------------------------- + +Use Linux and ALSA 0.5.x (or later) and Roland MT-32 while developing. +Don't implement supremely stupid abstract frameworks without consulting +experienced people on the mailinglist first. There are infinite ways to +implement the sound subsystem the VERY VERY WRONG way. Don't make +everything too much of a hack either. + +Use the files is in "lists/" when doing text output for debugging purposes. + +Good luck! diff --git a/engines/sci/sfx/old/main.c b/engines/sci/sfx/old/main.c new file mode 100644 index 0000000000..73daebd1bb --- /dev/null +++ b/engines/sci/sfx/old/main.c @@ -0,0 +1,49 @@ +/*************************************************************************** + main.c Copyright (C) 2000 Rickard Lind + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#include +#include +#include +#include +#include +#include "midi_mt32.h" + +int main() +{ + int fd; + unsigned char *patch; + unsigned int length; + + patch = (unsigned char *)sci_malloc(65536); + + fd = open("patch.001", O_RDONLY); + length = read(fd, patch, 65536); + close(fd); + + if (patch[0] == 0x89 && patch[1] == 0x00) + midi_mt32_open(patch + 2, length - 2); + else + midi_mt32_open(patch, length); + + midi_mt32_close(); + + free(patch); + return 0; +} diff --git a/engines/sci/sfx/old/midi.c b/engines/sci/sfx/old/midi.c new file mode 100644 index 0000000000..1be951befb --- /dev/null +++ b/engines/sci/sfx/old/midi.c @@ -0,0 +1,30 @@ +/*************************************************************************** + midi.c Copyright (C) 2000 Rickard Lind + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#ifndef _MIDI_MT32_H_ +#define _MIDI_MT32_H_ + +int midi_mt32_open(guint8 *data_ptr, unsigned int data_length); +int midi_mt32_close(); + +int midi_mt32_noteoff(guint8 channel, guint8 note); +int midi_mt32_noteon(guint8 channel, guint8 note, guint8 velocity); + +#endif /* _MIDI_MT32_H_ */ diff --git a/engines/sci/sfx/old/midi.h b/engines/sci/sfx/old/midi.h new file mode 100644 index 0000000000..9a816781d3 --- /dev/null +++ b/engines/sci/sfx/old/midi.h @@ -0,0 +1,30 @@ +/*************************************************************************** + midi.h Copyright (C) 2000 Rickard Lind + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#ifndef _MIDI_H_ +#define _MIDI_H_ + +int midi_open(guint8 *data_ptr, unsigned int data_length); +int midi_close(); + +int midi_noteoff(guint8 channel, guint8 note); +int midi_noteon(guint8 channel, guint8 note, guint8 velocity); + +#endif /* _MIDI_H_ */ diff --git a/engines/sci/sfx/old/midi_mt32.c b/engines/sci/sfx/old/midi_mt32.c new file mode 100644 index 0000000000..be5550bd93 --- /dev/null +++ b/engines/sci/sfx/old/midi_mt32.c @@ -0,0 +1,341 @@ +/*************************************************************************** + midi_mt32.c Copyright (C) 2000 Rickard Lind + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#include "glib.h" +#include +#include +#include +#include "midi_mt32.h" +#include "midiout.h" + +#define RHYTHM_CHANNEL 9 + +int midi_mt32_poke(guint32 address, guint8 *data, unsigned int n); +int midi_mt32_poke_gather(guint32 address, guint8 *data1, unsigned int count1, + guint8 *data2, unsigned int count2); +int midi_mt32_write_block(guint8 *data, unsigned int count); +int midi_mt32_patch001_type(guint8 *data, unsigned int length); +int midi_mt32_patch001_type0_length(guint8 *data, unsigned int length); +int midi_mt32_patch001_type1_length(guint8 *data, unsigned int length); +int midi_mt32_sysex_delay(); + +static guint8 *data; +static unsigned int length; +static int type; +static guint8 sysex_buffer[266] = {0xF0, 0x41, 0x10, 0x16, 0x12}; + +static gint8 patch_map[128] = { + 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}; +static gint8 keyshift[128] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +static gint8 volume_adjust[128] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +static guint8 velocity_map_index[128] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +static guint8 velocity_map[4][128] = { + {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}, + {0,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39, + 39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47, + 47,48,48,49,49,50,50,51,51,52,52,53,53,54,54,55, + 55,56,56,57,57,58,58,59,59,60,60,61,61,62,62,63, + 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}, + {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,66,67,67,68,68,69,69,70,70,71,71,72,72, + 73,73,74,74,75,75,76,76,77,77,78,78,79,79,80,80, + 81,81,82,82,83,83,84,84,85,85,86,86,87,87,88,88, + 89,89,90,90,91,91,92,92,93,93,94,94,95,95,96,96}, + {0,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39, + 39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47, + 47,48,48,49,49,50,50,51,51,52,52,53,53,54,54,55, + 55,56,56,57,57,58,58,59,59,60,60,61,61,62,62,63, + 64,65,66,66,67,67,68,68,69,69,70,70,71,71,72,72, + 73,73,74,74,75,75,76,76,77,77,78,78,79,79,80,80, + 81,81,82,82,83,83,84,84,85,85,86,86,87,87,88,88, + 89,89,90,90,91,91,92,92,93,93,94,94,95,95,96,96}}; +static gint8 rhythmkey_map[128] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,35,36,37,38,39,40,41,42,43,44,45,46,47, + 48,49,50,51,-1,-1,54,-1,56,-1,-1,-1,60,61,62,63, + 64,65,66,67,68,69,70,71,72,73,-1,75,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; +/* static struct { + gint8 sci_patch; + gint8 sci_volume; + gint8 sci_pitchwheel; + gint8 patch; + gint8 keyshift; + gint8 volume_adjust; + guint8 velocity_map_index; + guint8 +} channel[16]; */ +static guint8 master_volume; + +int midi_mt32_open(guint8 *data_ptr, unsigned int data_length) +{ + guint8 unknown_sysex[6] = {0x16, 0x16, 0x16, 0x16, 0x16, 0x16}; + guint8 i, memtimbres; + unsigned int block2, block3; + + if (midiout_open() < 0) + return -1; + + data = data_ptr; + length = data_length; + + type = midi_mt32_patch001_type(data, length); + printf("MT-32: Programming Roland MT-32 with patch.001 (v%i)\n", type); + + if (type == 0) { + /* Display MT-32 initialization message */ + printf("MT-32: Displaying Text: \"%.20s\"\n", data + 20); + midi_mt32_poke(0x200000, data + 20, 20); + midi_mt32_sysex_delay(); + + /* Write Patches (48 or 96) */ + memtimbres = data[491]; + block2 = (memtimbres * 246) + 492; + printf("MT-32: Writing Patches #01 - #32\n"); + midi_mt32_poke(0x050000, data + 107, 256); + midi_mt32_sysex_delay(); + if ((length > block2) && + data[block2] == 0xAB && + data[block2 + 1] == 0xCD) { + printf("MT-32: Writing Patches #33 - #64\n"); + midi_mt32_poke_gather(0x050200, data + 363, 128, data + block2 + 2, 128); + midi_mt32_sysex_delay(); + printf("MT-32: Writing Patches #65 - #96\n"); + midi_mt32_poke(0x050400, data + block2 + 130, 256); + midi_mt32_sysex_delay(); + block3 = block2 + 386; + } else { + printf("MT-32: Writing Patches #33 - #48\n"); + midi_mt32_poke(0x050200, data + 363, 128); + midi_mt32_sysex_delay(); + block3 = block2; + } + /* Write Memory Timbres */ + for (i = 0; i < memtimbres; i++) { + printf("MT-32: Writing Memory Timbre #%02d: \"%.10s\"\n", + i + 1, data + 492 + i * 246); + midi_mt32_poke(0x080000 + (i << 9), data + 492 + i * 246, 246); + midi_mt32_sysex_delay(); + } + /* Write Rhythm key map and Partial Reserve */ + if ((length > block3) && + data[block3] == 0xDC && + data[block3 + 1] == 0xBA) { + printf("MT-32: Writing Rhythm key map\n"); + midi_mt32_poke(0x030110, data + block3 + 2, 256); + midi_mt32_sysex_delay(); + printf("MT-32: Writing Partial Reserve\n"); + midi_mt32_poke(0x100004, data + block3 + 258, 9); + midi_mt32_sysex_delay(); + } + /* Display MT-32 initialization done message */ + printf("MT-32: Displaying Text: \"%.20s\"\n", data); + midi_mt32_poke(0x200000, data, 20); + midi_mt32_sysex_delay(); + /* Write undocumented MT-32(?) SysEx */ + printf("MT-32: Writing {F0 41 10 16 12 52 00 0A 16 16 16 16 16 16 20 F7}\n"); + midi_mt32_poke(0x52000A, unknown_sysex, 6); + midi_mt32_sysex_delay(); + return 0; + } else if (type == 1) { + midi_mt32_write_block(data + 1155, (data[1154] << 8) + data[1153]); + memcpy(patch_map, data, 128); + memcpy(keyshift, data + 128, 128); + memcpy(volume_adjust, data + 256, 128); + memcpy(velocity_map_index, data + 513, 128); + for (i = 0; i < 4; i++) + memcpy(velocity_map[i], data + 641 + i * 128, 128); + memcpy(rhythmkey_map, data + 384, 128); + return 0; + } + return -1; +} + +int midi_mt32_close() +{ +if (type == 0) { + printf("MT-32: Displaying Text: \"%.20s\"\n", data + 40); + midi_mt32_poke(0x200000, data + 40, 20); + midi_mt32_sysex_delay(); +} +return midiout_close(); +} + +int midi_mt32_noteoff(guint8 channel, guint8 note) +{ + return 0; +} + +int midi_mt32_noteon(guint8 channel, guint8 note, guint8 velocity) +{ +/* guint8 buffer[3] = {0x90}; + if (channel == RHYTHM_CHANNEL) + if (rhythmkey_map[note] == -1) + return 0; + else { + buffer[0] |= channel + buffer[1] = rhythmkey_map[note]; + buffer[2] = velo + midi_write_event(buffer, 3); + }; */ + return 0; +} + +int midi_mt32_poke(guint32 address, guint8 *data, unsigned int count) +{ + guint8 checksum = 0; + unsigned int i; + + if (count > 256) return -1; + + checksum -= (sysex_buffer[5] = (char)((address >> 16) & 0x7F)); + checksum -= (sysex_buffer[6] = (char)((address >> 8) & 0x7F)); + checksum -= (sysex_buffer[7] = (char)(address & 0x7F)); + + for (i = 0; i < count; i++) + checksum -= (sysex_buffer[i + 8] = data[i]); + + sysex_buffer[count + 8] = checksum & 0x7F; + sysex_buffer[count + 9] = 0xF7; + + return midiout_write_block(sysex_buffer, count + 10); +} + +int midi_mt32_poke_gather(guint32 address, guint8 *data1, unsigned int count1, + guint8 *data2, unsigned int count2) +{ + guint8 checksum = 0; + unsigned int i; + + if ((count1 + count2) > 256) return -1; + + checksum -= (sysex_buffer[5] = (char)((address >> 16) & 0x7F)); + checksum -= (sysex_buffer[6] = (char)((address >> 8) & 0x7F)); + checksum -= (sysex_buffer[7] = (char)(address & 0x7F)); + + for (i = 0; i < count1; i++) + checksum -= (sysex_buffer[i + 8] = data1[i]); + for (i = 0; i < count2; i++) + checksum -= (sysex_buffer[i + 8 + count1] = data2[i]); + + sysex_buffer[count1 + count2 + 8] = checksum & 0x7F; + sysex_buffer[count1 + count2 + 9] = 0xF7; + + return midiout_write_block(sysex_buffer, count1 + count2 + 10); +} + +int midi_mt32_write_block(guint8 *data, unsigned int count) +{ + unsigned int block_start = 0; + unsigned int i = 0; + + while (i < count) { + if ((data[i] == 0xF0) && (i != block_start)) { + midiout_write_block(data + block_start, i - block_start); + block_start = i; + } + if (data[i] == 0xF7) { + midiout_write_block(data + block_start, i - block_start + 1); + midi_mt32_sysex_delay(); + block_start = i + 1; + } + i++; + } + if (count >= block_start) + midiout_write_block(data + block_start, count - block_start); + return 0; +} + +int midi_mt32_patch001_type(guint8 *data, unsigned int length) +{ + /* length test */ + if (length < 1155) + return 0; + if (length > 16889) + return 1; + if (midi_mt32_patch001_type0_length(data, length) && + !midi_mt32_patch001_type1_length(data, length)) + return 0; + if (midi_mt32_patch001_type1_length(data, length) && + !midi_mt32_patch001_type0_length(data, length)) + return 1; + return -1; +} + +int midi_mt32_patch001_type0_length(guint8 *data, unsigned int length) +{ + unsigned int pos = 492 + 246 * data[491]; + + if ((length >= (pos + 386)) && (data[pos] == 0xAB) && (data[pos + 1] == 0xCD)) + pos += 386; + if ((length >= (pos + 267)) && (data[pos] == 0xDC) && (data[pos + 1] == 0xBA)) + pos += 267; + if (pos == length) + return 1; + return 0; +} + +int midi_mt32_patch001_type1_length(guint8 *data, unsigned int length) +{ + if ((length >= 1155) && (((data[1154] << 8) + data[1153] + 1155) == length)) + return 1; + return 0; +} + +int midi_mt32_sysex_delay() +{ + usleep(320 * 63); /* One MIDI byte is 320us, 320us * 63 > 20ms */ + return 0; +} diff --git a/engines/sci/sfx/old/midi_mt32.h b/engines/sci/sfx/old/midi_mt32.h new file mode 100644 index 0000000000..0e14efd41d --- /dev/null +++ b/engines/sci/sfx/old/midi_mt32.h @@ -0,0 +1,29 @@ +/*************************************************************************** + midi_mt32.h Copyright (C) 2000 Rickard Lind + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#ifndef _MIDI_MT32_H_ +#define _MIDI_MT32_H_ + +#include "glib.h" + +int midi_mt32_open(guint8 *data_ptr, unsigned int data_length); +int midi_mt32_close(); + +#endif /* _MIDI_MT32_H_ */ diff --git a/engines/sci/sfx/old/midiout.c b/engines/sci/sfx/old/midiout.c new file mode 100644 index 0000000000..262836baba --- /dev/null +++ b/engines/sci/sfx/old/midiout.c @@ -0,0 +1,63 @@ +/*************************************************************************** + midiout.c Copyright (C) 2000 Rickard Lind + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#include "glib.h" +#include "midiout.h" +#include "midiout_alsaraw.h" +#include "midiout_unixraw.h" + +static int (*midiout_ptr_close)(); +static int (*midiout_ptr_write)(guint8 *, unsigned int); + +static unsigned char running_status = 0; + +int midiout_open() +{ + midiout_ptr_close = midiout_alsaraw_close; + midiout_ptr_write = midiout_alsaraw_write; + return midiout_alsaraw_open(0, 0); + + /* + midiout_ptr_close = midiout_unixraw_close; + midiout_ptr_write = midiout_unixraw_write; + return midiout_unixraw_open("/dev/midi00"); + */ +} + +int midiout_close() +{ + return midiout_ptr_close(); +} + +int midiout_write_event(guint8 *buffer, unsigned int count) +{ + if (buffer[0] == running_status) + return midiout_ptr_write(buffer + 1, count - 1); + else { + running_status = buffer[0]; + return midiout_ptr_write(buffer, count); + } +} + +int midiout_write_block(guint8 *buffer, unsigned int count) +{ + running_status = 0; + return midiout_ptr_write(buffer, count); +} diff --git a/engines/sci/sfx/old/midiout.h b/engines/sci/sfx/old/midiout.h new file mode 100644 index 0000000000..e0ce3915ae --- /dev/null +++ b/engines/sci/sfx/old/midiout.h @@ -0,0 +1,29 @@ +/*************************************************************************** + midiout.h Copyright (C) 2000 Rickard Lind + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#ifndef _MIDIOUT_H_ +#define _MIDIOUT_H_ + +int midiout_open(); +int midiout_close(); +int midiout_write_event(guint8 *buffer, unsigned int count); +int midiout_write_block(guint8 *buffer, unsigned int count); + +#endif /* _MIDIOUT_H_ */ diff --git a/engines/sci/sfx/old/midiout_alsaraw.c b/engines/sci/sfx/old/midiout_alsaraw.c new file mode 100644 index 0000000000..e95aa28425 --- /dev/null +++ b/engines/sci/sfx/old/midiout_alsaraw.c @@ -0,0 +1,51 @@ +/*************************************************************************** + midiout_alsaraw.c Copyright (C) 2000 Rickard Lind + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#include "glib.h" +#include +#include +#include "midiout_alsaraw.h" + +static snd_rawmidi_t *handle; + +int midiout_alsaraw_open(int card, int device) +{ + int err; + + if ((err = snd_rawmidi_open(&handle, card, device, SND_RAWMIDI_OPEN_OUTPUT)) < 0) { + fprintf(stderr, "Open failed (%i): /dev/snd/midiC%iD%i\n", err, card, device); + return -1; + } + return 0; +} + +int midiout_alsaraw_close() +{ + if (snd_rawmidi_close(handle) < 0) + return -1; + return 0; +} + +int midiout_alsaraw_write(guint8 *buffer, unsigned int count) +{ + if (snd_rawmidi_write(handle, buffer, count) != count) + return -1; + return 0; +} diff --git a/engines/sci/sfx/old/midiout_alsaraw.h b/engines/sci/sfx/old/midiout_alsaraw.h new file mode 100644 index 0000000000..cbf9da44c1 --- /dev/null +++ b/engines/sci/sfx/old/midiout_alsaraw.h @@ -0,0 +1,28 @@ +/*************************************************************************** + midiout_alsaraw.h Copyright (C) 2000 Rickard Lind + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#ifndef _MIDIOUT_ALSARAW_H_ +#define _MIDIOUT_ALSARAW_H_ + +int midiout_alsaraw_open(int card, int device); +int midiout_alsaraw_close(); +int midiout_alsaraw_write(guint8 *buffer, unsigned int count); + +#endif /* _MIDIOUT_ALSARAW_H_ */ diff --git a/engines/sci/sfx/old/midiout_unixraw.c b/engines/sci/sfx/old/midiout_unixraw.c new file mode 100644 index 0000000000..a0dc85955f --- /dev/null +++ b/engines/sci/sfx/old/midiout_unixraw.c @@ -0,0 +1,52 @@ +/*************************************************************************** + midiout_unixraw.c Copyright (C) 2000 Rickard Lind + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#include "glib.h" +#include +#include +#include +#include +#include +#include "midiout_unixraw.h" + +static int fd; + +int midiout_unixraw_open(char *devicename) +{ + if (!IS_VALID_FD(fd = open(devicename, O_WRONLY|O_SYNC))) { + fprintf(stderr, "Open failed (%i): %s\n", fd, devicename); + return -1; + } + return 0; +} + +int midiout_unixraw_close() +{ + if (close(fd) < 0) + return -1; + return 0; +} + +int midiout_unixraw_write(guint8 *buffer, unsigned int count) +{ + if (write(fd, buffer, count) != count) + return -1; + return 0; +} diff --git a/engines/sci/sfx/old/midiout_unixraw.h b/engines/sci/sfx/old/midiout_unixraw.h new file mode 100644 index 0000000000..c5980696e9 --- /dev/null +++ b/engines/sci/sfx/old/midiout_unixraw.h @@ -0,0 +1,28 @@ +/*************************************************************************** + midiout_unixraw.h Copyright (C) 2000 Rickard Lind + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#ifndef _MIDIOUT_UNIXRAW_H_ +#define _MIDIOUT_UNIXRAW_H_ + +int midiout_unixraw_open(char *devicename); +int midiout_unixraw_close(); +int midiout_unixraw_write(guint8 *buffer, unsigned int count); + +#endif /* _MIDIOUT_UNIXRAW_H_ */ diff --git a/engines/sci/sfx/pcm-iterator.c b/engines/sci/sfx/pcm-iterator.c new file mode 100644 index 0000000000..8d9b86ed0d --- /dev/null +++ b/engines/sci/sfx/pcm-iterator.c @@ -0,0 +1,117 @@ +/*************************************************************************** + pcm-iterator.c Copyright (C) 2002 Christoph Reichenbach + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + + Please contact the maintainer for any program-related bug reports or + inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include /* for BREAKPOINT */ +#include + +#define D ((pcm_data_internal_t *)self->internal) + +static int +pi_poll(sfx_pcm_feed_t *self, byte *dest, int size); +static void +pi_destroy(sfx_pcm_feed_t *self); + +typedef struct { + byte *base_data; + byte *data; + int frames_left; +} pcm_data_internal_t; + + +static sfx_pcm_feed_t pcm_it_prototype = { + pi_poll, + pi_destroy, + NULL, /* No timestamp getter */ + NULL, /* Internal data goes here */ + {0,0,0}, /* Must fill in configuration */ + "song-iterator", + 0, /* Ideally the resource number should go here */ + 0 /* The mixer computes this for us */ +}; + + +sfx_pcm_feed_t * +sfx_iterator_make_feed(byte *base_data, + int offset, + int size, + sfx_pcm_config_t conf) +{ + sfx_pcm_feed_t *feed; + pcm_data_internal_t *idat; + byte *data = base_data + offset; + + if (!data) { + /* Now this is silly; why'd you call this function in the first place? */ + return NULL; + } + sci_refcount_incref(base_data); + + idat = (pcm_data_internal_t*)sci_malloc(sizeof(pcm_data_internal_t)); + idat->base_data = base_data; + idat->data = data; + idat->frames_left = size; + feed = (sfx_pcm_feed_t*)sci_malloc(sizeof(sfx_pcm_feed_t)); + *feed = pcm_it_prototype; + feed->internal = idat; + feed->conf = conf; + + return feed; +} + + +static int +pi_poll(sfx_pcm_feed_t *self, byte *dest, int size) +{ + int data_len; + + if (size >= D->frames_left) + size = D->frames_left; + + D->frames_left -= size; + + data_len = size * self->frame_size; + + memcpy(dest, D->data, data_len); +#if 0 +memset(dest, 0xff, data_len); +#endif + + D->data += data_len; + + return size; +} + +static void +pi_destroy(sfx_pcm_feed_t *self) +{ + sci_refcount_decref(D->base_data); + sci_free(D); + sci_free(self); +} diff --git a/engines/sci/sfx/pcm_device/Makefile.am b/engines/sci/sfx/pcm_device/Makefile.am new file mode 100644 index 0000000000..b13a3aab0b --- /dev/null +++ b/engines/sci/sfx/pcm_device/Makefile.am @@ -0,0 +1,7 @@ +noinst_LIBRARIES = libscipcm.a +INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@ +AM_CFLAGS = $(SDL_CFLAGS) +EXTRA_DIST = audiobuf.h +libscipcm_a_SOURCES = audiobuf.c pcm_devices.c sdl.c alsa.c +audbuf_test_LDADD = libscipcm.a ../../scicore/libscicore.a +check_PROGRAMS = audbuf_test diff --git a/engines/sci/sfx/pcm_device/alsa.c b/engines/sci/sfx/pcm_device/alsa.c new file mode 100644 index 0000000000..26d4c35044 --- /dev/null +++ b/engines/sci/sfx/pcm_device/alsa.c @@ -0,0 +1,387 @@ +/*************************************************************************** + alsa.c Copyright (C) 2004 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +/* Based on ALSA's pcm.c example. */ + +#include +#include "audiobuf.h" + +#ifdef HAVE_ALSA +#ifdef HAVE_PTHREAD + +#define ALSA_PCM_NEW_HW_PARAMS_API +#define ALSA_PCM_NEW_SW_PARAMS_API + +#include +#include + +static const char *device = "default"; /* FIXME */ +static snd_pcm_format_t format = SND_PCM_FORMAT_S16; +static unsigned int rate = 44100; /* FIXME */ +static unsigned int channels = 2; /* FIXME */ +static unsigned int buffer_time = 100000; /* FIXME */ +static unsigned int period_time = 1000000/60; /* 60Hz */ /* FIXME */ + +static snd_pcm_sframes_t buffer_size; +static snd_pcm_sframes_t period_size; + +static int frame_size; +static long last_callback_secs, last_callback_usecs; + +static sfx_audio_buf_t audio_buffer; +static void (*alsa_sfx_timer_callback)(void *data); +static void *alsa_sfx_timer_data; + +static snd_pcm_t *handle; + +static pthread_t thread; +static volatile byte run_thread; + +static pthread_mutex_t mutex; + +static int +xrun_recovery(snd_pcm_t *handle, int err) +{ + if (err == -EPIPE) { /* under-run */ + err = snd_pcm_prepare(handle); + if (err < 0) + fprintf(stderr, "Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err)); + return 0; + } else if (err == -ESTRPIPE) { + while ((err = snd_pcm_resume(handle)) == -EAGAIN) + sleep(1); /* wait until the suspend flag is released */ + if (err < 0) { + err = snd_pcm_prepare(handle); + if (err < 0) + fprintf(stderr, "Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err)); + } + return 0; + } + return err; +} + +static void * +alsa_thread(void *arg) +{ + gint16 *ptr; + int err, cptr; + guint8 *buf; + sfx_pcm_device_t *self = (sfx_pcm_device_t *) arg; + + buf = (guint8 *) malloc(period_size * frame_size); + + while (run_thread) { + ptr = (gint16 *) buf; + cptr = period_size; + + sci_gettime(&last_callback_secs, &last_callback_usecs); + + self->timer->block(); + + if (alsa_sfx_timer_callback) + alsa_sfx_timer_callback(alsa_sfx_timer_data); + + self->timer->unblock(); + + sfx_audbuf_read(&audio_buffer, buf, period_size); + + while (cptr > 0) { + err = snd_pcm_writei(handle, ptr, cptr); + if (err == -EAGAIN) + continue; + if (err < 0) { + if (xrun_recovery(handle, err) < 0) { + fprintf(stderr, "[SND:ALSA] Write error: %s\n", snd_strerror(err)); + run_thread = 0; + } + break; /* skip one period */ + } + ptr += err * channels; + cptr -= err; + } + } + + free(buf); + return NULL; +} + +static sfx_timestamp_t +pcmout_alsa_output_timestamp(sfx_pcm_device_t *self) +{ + /* Number of frames enqueued in the output device: */ + int delta = (buffer_size - period_size) / frame_size + /* Number of frames enqueued in the internal audio buffer: */ + + audio_buffer.frames_nr; + + return sfx_timestamp_add(sfx_new_timestamp(last_callback_secs, + last_callback_usecs, + rate), + delta); +} + +static int +pcmout_alsa_init(sfx_pcm_device_t *self) +{ + unsigned int rrate; + int err, dir; + snd_pcm_hw_params_t *hwparams; + snd_pcm_sw_params_t *swparams; + pthread_attr_t attr; + + snd_pcm_hw_params_alloca(&hwparams); + snd_pcm_sw_params_alloca(&swparams); + + err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0); + if (err < 0) { + sciprintf("[SND:ALSA] Playback open error: %s\n", snd_strerror(err)); + return SFX_ERROR; + } + err = snd_pcm_hw_params_any(handle, hwparams); + if (err < 0) { + sciprintf("[SND:ALSA] Broken configuration for playback: no configurations available: %s\n", snd_strerror(err)); + return SFX_ERROR; + } + err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); + if (err < 0) { + sciprintf("[SND:ALSA] Access type not available for playback: %s\n", snd_strerror(err)); + return SFX_ERROR; + } + err = snd_pcm_hw_params_set_format(handle, hwparams, format); + if (err < 0) { + sciprintf("[SND:ALSA] Sample format not available for playback: %s\n", snd_strerror(err)); + return SFX_ERROR; + } + err = snd_pcm_hw_params_set_channels(handle, hwparams, channels); + if (err < 0) { + sciprintf("[SND:ALSA] Channels count (%i) not available for playback: %s\n", channels, snd_strerror(err)); + return SFX_ERROR; + } + rrate = rate; + err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &rrate, 0); + if (err < 0) { + sciprintf("[SND:ALSA] Rate %iHz not available for playback: %s\n", rate, snd_strerror(err)); + return SFX_ERROR; + } + if (rrate != rate) { + sciprintf("[SND:ALSA] Rate doesn't match (requested %iHz, get %iHz)\n", rate, err); + return SFX_ERROR; + } + err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir); + if (err < 0) { + sciprintf("[SND:ALSA] Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err)); + return SFX_ERROR; + } + err = snd_pcm_hw_params_get_buffer_size(hwparams, (snd_pcm_uframes_t*)&buffer_size); + if (err < 0) { + sciprintf("[SND:ALSA] Unable to get buffer size for playback: %s\n", snd_strerror(err)); + return SFX_ERROR; + } + err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir); + if (err < 0) { + sciprintf("[SND:ALSA] Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err)); + return SFX_ERROR; + } + err = snd_pcm_hw_params_get_period_size(hwparams, (snd_pcm_uframes_t*)&period_size, &dir); + if (err < 0) { + sciprintf("[SND:ALSA] Unable to get period size for playback: %s\n", snd_strerror(err)); + return SFX_ERROR; + } + if (period_size >= buffer_size) { + sciprintf("[SND:ALSA] Period size %i matches or exceeds buffer size %i\n", period_size, buffer_size); + return SFX_ERROR; + } + err = snd_pcm_hw_params(handle, hwparams); + if (err < 0) { + sciprintf("[SND:ALSA] Unable to set hw params for playback: %s\n", snd_strerror(err)); + return SFX_ERROR; + } + err = snd_pcm_sw_params_current(handle, swparams); + if (err < 0) { + sciprintf("[SND:ALSA] Unable to determine current swparams for playback: %s\n", snd_strerror(err)); + return SFX_ERROR; + } + err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size); + if (err < 0) { + sciprintf("[SND:ALSA] Unable to set start threshold mode for playback: %s\n", snd_strerror(err)); + return SFX_ERROR; + } + err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size); + if (err < 0) { + sciprintf("[SND:ALSA] Unable to set avail min for playback: %s\n", snd_strerror(err)); + return SFX_ERROR; + } + err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1); + if (err < 0) { + sciprintf("[SND:ALSA] Unable to set transfer align for playback: %s\n", snd_strerror(err)); + return SFX_ERROR; + } + err = snd_pcm_sw_params(handle, swparams); + if (err < 0) { + sciprintf("[SND:ALSA] Unable to set sw params for playback: %s\n", snd_strerror(err)); + return SFX_ERROR; + } + + self->buf_size = buffer_size; + self->conf.rate = rate; + self->conf.stereo = channels > 1; + self->conf.format = SFX_PCM_FORMAT_S16_NATIVE; + + frame_size = SFX_PCM_FRAME_SIZE(self->conf); + + sfx_audbuf_init(&audio_buffer, self->conf); + + if (pthread_mutex_init(&mutex, NULL) != 0) { + sciprintf("[SND:ALSA] Failed to create mutex\n"); + return SFX_ERROR; + } + + run_thread = 1; + if (pthread_create(&thread, NULL, alsa_thread, self) != 0) { + sciprintf("[SND:ALSA] Failed to create thread\n"); + return SFX_ERROR; + } + + return SFX_OK; +} + +static int +pcmout_alsa_output(sfx_pcm_device_t *self, byte *buf, + int count, sfx_timestamp_t *ts) +{ + if (ts) + sfx_audbuf_write_timestamp(&audio_buffer, *ts); + + sfx_audbuf_write(&audio_buffer, buf, count); + return SFX_OK; +} + +static int +pcmout_alsa_set_option(sfx_pcm_device_t *self, char *name, char *value) +{ + return SFX_ERROR; +} + +static void +pcmout_alsa_exit(sfx_pcm_device_t *self) +{ + int err; + + run_thread = 0; + sciprintf("[SND:ALSA] Waiting for PCM thread to exit... "); + if (!pthread_join(thread, NULL)) + sciprintf("OK\n"); + else + sciprintf("Failed\n"); + + pthread_mutex_destroy(&mutex); + + if ((err = snd_pcm_drop(handle)) < 0) { + sciprintf("[SND:ALSA] Can't stop PCM device: %s\n", snd_strerror(err)); + } + if ((err = snd_pcm_close(handle)) < 0) { + sciprintf("[SND:ALSA] Can't close PCM device: %s\n", snd_strerror(err)); + } + + sfx_audbuf_free(&audio_buffer); +} + +static int +timer_alsa_set_option(char *name, char *value) +{ + return SFX_ERROR; +} + + +static int +timer_alsa_init(void (*callback)(void *data), void *data) +{ + alsa_sfx_timer_callback = callback; + alsa_sfx_timer_data = data; + + return SFX_OK; +} + +static int +timer_alsa_stop(void) +{ + alsa_sfx_timer_callback = NULL; + + return SFX_OK; +} + +static int +timer_alsa_block(void) +{ + if (pthread_mutex_lock(&mutex) != 0) { + fprintf(stderr, "[SND:ALSA] Failed to lock mutex\n"); + return SFX_ERROR; + } + + return SFX_OK; +} + +static int +timer_alsa_unblock(void) +{ + if (pthread_mutex_unlock(&mutex) != 0) { + fprintf(stderr, "[SND:ALSA] Failed to unlock mutex\n"); + return SFX_ERROR; + } + + return SFX_OK; +} + +#define ALSA_PCM_VERSION "0.2" + +sfx_timer_t pcmout_alsa_timer = { + "alsa-pcm-timer", + ALSA_PCM_VERSION, + 0, + 0, + timer_alsa_set_option, + timer_alsa_init, + timer_alsa_stop, + timer_alsa_block, + timer_alsa_unblock +}; + +sfx_pcm_device_t sfx_pcm_driver_alsa = { + "alsa", + ALSA_PCM_VERSION, + pcmout_alsa_init, + pcmout_alsa_exit, + pcmout_alsa_set_option, + pcmout_alsa_output, + pcmout_alsa_output_timestamp, + {0, 0, 0}, + 0, + &pcmout_alsa_timer, + NULL +}; + +#endif /* HAVE_PTHREAD */ +#endif /* HAVE_ALSA */ diff --git a/engines/sci/sfx/pcm_device/audbuf_test.c b/engines/sci/sfx/pcm_device/audbuf_test.c new file mode 100644 index 0000000000..216aa9079e --- /dev/null +++ b/engines/sci/sfx/pcm_device/audbuf_test.c @@ -0,0 +1,190 @@ +/*************************************************************************** + audbuf_test.c Copyright (C) 2002 Christoph Reichenbach + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + + Please contact the maintainer for any program-related bug reports or + inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include "audiobuf.h" +#if 0 +sfx_audio_buf_t buf; + +#define MIN_FRAMESIZE 1 +#define MAX_FRAMESIZE 8 + + +void +tester_write(unsigned char *data, int datalen, int framesize, int gran) +{ + int i; + + for (i = 0; i < datalen; i += gran) { + int size = (i + gran < datalen)? gran : datalen - i; + + sfx_audbuf_write(&buf, data + (i * framesize), framesize, size); + } +} + + +void +tester_read(unsigned char *data, int datalen, int framesize, int gran) +{ + unsigned char *readdata = malloc(datalen * framesize); + int i; + + for (i = 0; i < datalen; i += gran) { + int size = (i + gran < datalen)? gran : datalen - i; + int j; + + sfx_audbuf_read(&buf, readdata + (i * framesize), framesize, size); + for (j = 0; j < gran * framesize; j++) { + int offset = i*framesize + j; + + if (data[i] != readdata[i]) { + fprintf(stderr, "[ERROR] Mismatch at offset %08x (sample #%d): Expected %02x, got %02x\n", + offset, i, readdata[i], data[i]); + } + } + } + + free(readdata); +} + + +void +test1(unsigned char *data, int len) + /* Test the 'regular' case */ +{ + int framesize; + int stepsize; + + fprintf(stderr, "[Test-1] Commenced; len=%d.\n", len); + + for (framesize = MAX_FRAMESIZE; framesize >= MIN_FRAMESIZE; framesize >>= 1) { + fprintf(stderr, "[Test-1] Writing frame size %d\n", framesize); + for (stepsize = 1; stepsize <= len; stepsize++) + tester_write(data, len / framesize, framesize, stepsize); + } + + for (framesize = MAX_FRAMESIZE; framesize >= MIN_FRAMESIZE; framesize >>= 1) { + fprintf(stderr, "[Test-1] Reading frame size %d\n", framesize); + for (stepsize = len; stepsize >= 1; stepsize--) + tester_read(data, len / framesize, framesize, stepsize); + } + + fprintf(stderr, "[Test-1] Completed.\n"); +} + +#define TEST2_COUNT 10 +#define TEST2_LEN 3 + +void +test2(unsigned char *data, int framesize) + /* Test whether buffer underrun repeats are handled correctly */ +{ + int i; + unsigned char *src; + + fprintf(stderr, "[Test-2] Commenced; framesize=%d.\n", framesize); + + sfx_audbuf_write(&buf, data, framesize, 1); + src = malloc(framesize * TEST2_LEN); + + for (i = 0; i < TEST2_COUNT + 1; i++) { + int inst; + sfx_audbuf_read(&buf, src, framesize, TEST2_LEN); + + for (inst = 0; inst < TEST2_LEN; inst++) { + int offset = inst * framesize; + int j; + + for (j = 0; j < framesize; j++) + if (src[j + offset] != data[j]) { + fprintf(stderr, "[ERROR] At copy sample %d, frame %d, offset %d: Expected %02x, got %02x\n", + i, inst, j, data[j], src[j+offset]); + } + } + memset(src, 0xbf, framesize * TEST2_LEN); + } + + free(src); + fprintf(stderr, "[Test-1] Completed.\n"); +} + + +#define CHUNKS_NR 4 + +#define CHUNK_LEN_0 8 +#define CHUNK_LEN_1 20 +#define CHUNK_LEN_2 16 +#define CHUNK_LEN_3 40 + +unsigned char test_data_0[CHUNK_LEN_0] = + { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; +unsigned char test_data_1[CHUNK_LEN_1] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, + 0xff, 0x00, 0xff, 0x00}; +unsigned char test_data_2[CHUNK_LEN_2] = + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +unsigned char test_data_3[CHUNK_LEN_3] = + { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88 }; + +struct { + int len; + unsigned char *data; +} test_chunks[CHUNKS_NR] = { + { CHUNK_LEN_0, test_data_0 }, + { CHUNK_LEN_1, test_data_1 }, + { CHUNK_LEN_2, test_data_2 }, + { CHUNK_LEN_3, test_data_3 } +}; + +int +main(int argc, char **argv) +{ + int i; + + sfx_audbuf_init(&buf); + for (i = 0; i < CHUNKS_NR; i++) { + int k; + + /* for (k = MAX_FRAMESIZE; k >= MIN_FRAMESIZE; k >>= 1) + test2(test_chunks[i].data, k);*/ + + test1(test_chunks[i].data, test_chunks[i].len); + } + sfx_audbuf_exit(&buf); + + return 0; +} +#else +int main() {} +#endif diff --git a/engines/sci/sfx/pcm_device/audiobuf.c b/engines/sci/sfx/pcm_device/audiobuf.c new file mode 100644 index 0000000000..fa23708ce4 --- /dev/null +++ b/engines/sci/sfx/pcm_device/audiobuf.c @@ -0,0 +1,348 @@ +/*************************************************************************** + audiobuf.c Copyright (C) 2003 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include "audiobuf.h" + +#define NO_BUFFER_UNDERRUN 0 +#define SAW_BUFFER_UNDERRUN 1 +#define REPORTED_BUFFER_UNDERRUN 2 + +static int +buffer_underrun_status = NO_BUFFER_UNDERRUN; + + +static sfx_audio_buf_chunk_t * +sfx_audbuf_alloc_chunk(void) +{ + sfx_audio_buf_chunk_t *ch = (sfx_audio_buf_chunk_t*)sci_malloc(sizeof(sfx_audio_buf_chunk_t)); + ch->used = 0; + ch->next = NULL; + ch->prev = NULL; + + return ch; +} + +void +sfx_audbuf_init(sfx_audio_buf_t *buf, sfx_pcm_config_t pcm_conf) +{ + int framesize = SFX_PCM_FRAME_SIZE(pcm_conf); + byte silence[16]; + int silencew = pcm_conf.format & ~SFX_PCM_FORMAT_LMASK; + + /* Determine the correct 'silence' for the channel and install it */ + /* Conservatively assume stereo */ + if (pcm_conf.format & SFX_PCM_FORMAT_16) { + if (pcm_conf.format & SFX_PCM_FORMAT_LE) { + silence[0] = silencew & 0xff; + silence[1] = (silencew >> 8) & 0xff; + } else { + silence[0] = (silencew >> 8) & 0xff; + silence[1] = silencew & 0xff; + } + memcpy(silence + 2, silence, 2); + } else { + silence[0] = silencew; + silence[1] = silencew; + } + + buf->last = buf->first = sfx_audbuf_alloc_chunk(); + buf->unused = NULL; + memcpy(buf->last_frame, silence, framesize); /* Initialise, in case we + ** underrun before the + ** first write */ + buf->read_offset = 0; + buf->framesize = framesize; + buf->read_timestamp.secs = -1; /* Mark as inactive */ + buf->frames_nr = 0; +} + +static void +sfx_audbuf_free_chain(sfx_audio_buf_chunk_t *b) +{ + while (b) { + sfx_audio_buf_chunk_t *n = b->next; + sci_free(b); + b = n; + } +} + +void +sfx_audbuf_free(sfx_audio_buf_t *buf) +{ + sfx_audbuf_free_chain(buf->first); + sfx_audbuf_free_chain(buf->unused); + buf->first = buf->last = buf->unused = NULL; + buf->read_offset = (int) 0xdeadbeef; +} + +void +sfx_audbuf_write(sfx_audio_buf_t *buf, unsigned char *src, int frames) +{ + /* In here, we compute PER BYTE */ + int data_left = buf->framesize * frames; + + if (!buf->last) { + fprintf(stderr, "FATAL: Violation of audiobuf.h usage protocol: Must use 'init' before 'write'\n"); + exit(1); + } + + if (buffer_underrun_status == SAW_BUFFER_UNDERRUN) { + /* Print here to avoid threadsafeness issues */ + sciprintf("[audiobuf] Buffer underrun\n"); + buffer_underrun_status = REPORTED_BUFFER_UNDERRUN; + } + + buf->frames_nr += frames; + + while (data_left) { + int cpsize; + int buf_free; + buf_free = SFX_AUDIO_BUF_SIZE - buf->last->used; + + + if (buf_free >= data_left) + cpsize = data_left; + else + cpsize = buf_free; + + /* Copy and advance pointers */ + memcpy(buf->last->data + buf->last->used, src, cpsize); + data_left -= cpsize; + buf->last->used += cpsize; + src += cpsize; + + if (buf->last->used == SFX_AUDIO_BUF_SIZE) { + if (!buf->last->next) { + sfx_audio_buf_chunk_t *old = buf->last; + if (buf->unused) { /* Re-use old chunks */ + sfx_audio_buf_chunk_t *buf_next_unused = buf->unused->next; + buf->unused->next = NULL; + buf->unused->used = 0; + + buf->last->next = buf->unused; + buf->unused = buf_next_unused; + } else /* Allocate */ + buf->last->next = + sfx_audbuf_alloc_chunk(); + + buf->last->prev = old; + } + + buf->last = buf->last->next; + } + } + +#ifdef TRACE_BUFFER + { + sfx_audio_buf_chunk_t *c = buf->first; + int t = buf->read_offset; + + while (c) { + fprintf(stderr, "-> ["); + for (; t < c->used; t++) + fprintf(stderr, " %02x", c->data[t]); + t = 0; + fprintf(stderr, " ] "); + c = c->next; + } + fprintf(stderr, "\n"); + } +#endif + + if (frames && (src - buf->framesize) != buf->last_frame) + /* Backup last frame, unless we're already filling from it */ + memcpy(buf->last_frame, src - buf->framesize, buf->framesize); +} + +int +sfx_audbuf_read(sfx_audio_buf_t *buf, unsigned char *dest, int frames) +{ + int written = 0; + + if (frames <= 0) + return 0; + + if (buf->read_timestamp.secs >= 0) { + /* Have a timestamp? Must update it! */ + buf->read_timestamp = + sfx_timestamp_add(buf->read_timestamp, + frames); + + } + + buf->frames_nr -= frames; + if (buf->frames_nr < 0) + buf->frames_nr = 0; + +#ifdef TRACE_BUFFER + { + sfx_audio_buf_chunk_t *c = buf->first; + int t = buf->read_offset; + + while (c) { + fprintf(stderr, "-> ["); + for (; t < c->used; t++) + fprintf(stderr, " %02x", c->data[t]); + t = 0; + fprintf(stderr, " ] "); + c = c->next; + } + fprintf(stderr, "\n"); + } +#endif + + while (frames) { + int data_needed = frames * buf->framesize; + int rdbytes = data_needed; + int rdframes; + + if (rdbytes > buf->first->used - buf->read_offset) + rdbytes = buf->first->used - buf->read_offset; + + memcpy(dest, buf->first->data + buf->read_offset, rdbytes); + + buf->read_offset += rdbytes; + dest += rdbytes; + + if (buf->read_offset == SFX_AUDIO_BUF_SIZE) { + /* Continue to next, enqueue the current chunk as + ** being unused */ + sfx_audio_buf_chunk_t *lastfirst = buf->first; + + buf->first = buf->first->next; + lastfirst->next = buf->unused; + buf->unused = lastfirst; + + buf->read_offset = 0; + } + + rdframes = (rdbytes / buf->framesize); + frames -= rdframes; + written += rdframes; + + if (frames && + (!buf->first || buf->read_offset == buf->first->used)) { + fprintf(stderr, "Underrun by %d frames at %d\n", frames, + buf->read_timestamp.frame_rate); + /* Buffer underrun! */ + if (!buffer_underrun_status == NO_BUFFER_UNDERRUN) { + buffer_underrun_status = SAW_BUFFER_UNDERRUN; + } + do { + memcpy(dest, buf->last_frame, buf->framesize); + dest += buf->framesize; + } while (--frames); + } + + } + + return written; +} + +#if 0 +static void +_sfx_audbuf_rewind_stream(sfx_audio_buf_t *buf, int delta) +{ + if (delta > buf->frames_nr) + delta = buf->frames_nr; + + + fprintf(stderr, "Rewinding %d\n", delta); + buf->frames_nr -= delta; + + /* From here on, 'delta' means the number of BYTES to remove */ + delta *= buf->framesize; + + while (delta) { + if (buf->last->used >= delta) { + fprintf(stderr, "Subtracting from %d %d\n", buf->last->used, delta); + buf->last->used -= delta; + delta = 0; + } else { + fprintf(stderr, "Must do block-unuse\n"); + delta -= buf->last->used; + buf->last->used = 0; + buf->last->next = buf->unused; + buf->unused = buf->last; + buf->last = buf->unused->prev; + buf->unused->prev = NULL; + } + } +} +#endif + +void +sfx_audbuf_write_timestamp(sfx_audio_buf_t *buf, sfx_timestamp_t ts) +{ + sfx_timestamp_t newstamp; + + newstamp = sfx_timestamp_add(ts, -buf->frames_nr); + + + if (buf->read_timestamp.secs <= 0) + /* Initial stamp */ + buf->read_timestamp = newstamp; + else { + int delta = sfx_timestamp_frame_diff(newstamp, buf->read_timestamp); + long s1,s2,s3,u1,u2,u3; + sfx_timestamp_gettime(&(buf->read_timestamp), &s1, &u1); + sfx_timestamp_gettime(&(newstamp), &s2, &u2); + sfx_timestamp_gettime(&(ts), &s3, &u3); + + if (delta < 0) { +#if 0 + /* fprintf(stderr, "[SFX-BUF] audiobuf.c: Timestamp delta %d at %d: Must rewind (not implemented yet)\n", + delta, buf->read_timestamp.frame_rate);*/ + _sfx_audbuf_rewind_stream(buf, -delta); + buf->read_timestamp = newstamp; +#endif + } else if (delta > 0) { + fprintf(stderr, "[SFX-BUF] audiobuf.c: Timestamp delta %d at %d: Filling in as silence frames\n", + delta, buf->read_timestamp.frame_rate); + /* Fill up with silence */ + while (delta--) { + sfx_audbuf_write(buf, buf->last_frame, 1); + } + buf->read_timestamp = newstamp; + } + } +} + +int +sfx_audbuf_read_timestamp(sfx_audio_buf_t *buf, sfx_timestamp_t *ts) +{ + if (buf->read_timestamp.secs > 0) { + *ts = buf->read_timestamp; + return 0; + } else { + ts->secs = -1; + ts->usecs = -1; + ts->frame_offset = -1; + ts->frame_rate = -1; + return 1; /* No timestamp */ + } +} diff --git a/engines/sci/sfx/pcm_device/audiobuf.h b/engines/sci/sfx/pcm_device/audiobuf.h new file mode 100644 index 0000000000..b2019aec17 --- /dev/null +++ b/engines/sci/sfx/pcm_device/audiobuf.h @@ -0,0 +1,140 @@ +/*************************************************************************** + audiobuf.h Copyright (C) 2003 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +/* Auxiliary audio buffer for PCM devices +** Polled PCM devices must store data written to them until it is explicitly +** requiested. This is facilitated by the structures and functions defined +** here. +** This is generic for all PCM devices; it implies no specific requirements. +** +** Usage: Introduce an sfx_audio_buf_t into your state and make sure to use +** each of the functions provided here at least once in the appropriate +** places. +*/ + + +#ifndef _AUDIOBUF_H_ +#define _AUDIOBUF_H_ + +#include +#include +#include +#include + + +#define SFX_AUDIO_BUF_SIZE 8192 /* Must be multiple of framesize */ +#define SFX_AUDIO_MAX_FRAME 8 /* Max. individual frame size */ + +typedef struct _sfx_audio_buf_chunk { + unsigned char data[SFX_AUDIO_BUF_SIZE]; + int used; + struct _sfx_audio_buf_chunk *prev; + struct _sfx_audio_buf_chunk *next; +} sfx_audio_buf_chunk_t; + +typedef struct { + int read_offset; + sfx_audio_buf_chunk_t *first; /* Next to read-- can be = last */ + sfx_audio_buf_chunk_t *last; /* Next to write-- can be = first */ + sfx_audio_buf_chunk_t *unused; /* Unused chunk list, can be NULL */ + unsigned char last_frame[SFX_AUDIO_MAX_FRAME]; + /* Contains the last frame successfully read; used for buffer + ** underruns to avoid crack before silance */ + sfx_timestamp_t read_timestamp; /* Timestamp for reading */ + int frames_nr; /* Total number of frames currently in between reading and writing */ + int framesize; +} sfx_audio_buf_t; + + +void +sfx_audbuf_init(sfx_audio_buf_t *buf, sfx_pcm_config_t conf); +/* Initialises an audio buffer +** Parameters: (sfx_audio_buf_t *) buf: The buffer to initialise +** (sfx_pcm_config_t) conf: The configuration for which the buffer should +** be set up +** Modifies : (sfx_audio_buf_t) *buf +*/ + +void +sfx_audbuf_free(sfx_audio_buf_t *buf); +/* Frees all memory associated with an audio buffer +** Parameters: (sfx_audio_buf_t *) buf: The buffer whose associated memory +** should be freed +** Modifies : (sfx_audio_buf_t) *buf +*/ + +void +sfx_audbuf_write(sfx_audio_buf_t *buf, unsigned char *src, int frames); +/* Store data in an audion buffer +** Parameters: (sfx_audio_buf_t *) buf: The buffer to write to +** (unsigned char *) src: Pointer to the data that should be +** written +** (int) frames: Number of frames to write +** Modifies : (sfx_audio_buf_t) *buf +*/ + + +void +sfx_audbuf_write_timestamp(sfx_audio_buf_t *buf, sfx_timestamp_t ts); +/* Sets the most recently written timestamp for the buffer +** Parameters: (sfx_audio_buf_t *) buf: The buffer to operate on +** (sfx_timestamp_t) ts: The timestamp to set +** If a timestamp is already set, 'ts' is checked for consistency and +** 'silent' frames are introduced as padding for future writes. +*/ + + +int +sfx_audbuf_read_timestamp(sfx_audio_buf_t *buf, sfx_timestamp_t *ts); +/* Reads the timestamp describing the time right before the next frame being read +** Parameters: (sfx_audio_buf_t *) buf: The buffer to read from +** Returns : (sfx_timestamp_t) *ts: The requested timestamp, or nothing +** (int) zero on success, nonzero if no timestamp is known +*/ + + +int +sfx_audbuf_read(sfx_audio_buf_t *buf, unsigned char *dest, int frames); +/* Read data from audio buffer +** Parameters: (sfx_audio_buf_t *) buf: The buffer to write to +** (unsigned char *) dest: Pointer to the place the read data +** should be written to +** (int) frames: Number of frames to write +** Returns : (int) Number of frames actually read +** Affects : (sfx_audio_buf_t) *buf +** (unsigned char ) *dest +** global error stream +** If the returned number of frames is smaller than the number of frames +** requested to be written, this function will issue a buffer underrun +** warning and fill up the remaining space with the last frame it en-- +** countered, or a block of '0' if no such frame is known. +*/ + + + + +#endif /* !_AUDIOBUF_H_ */ diff --git a/engines/sci/sfx/pcm_device/pcm_devices.c b/engines/sci/sfx/pcm_device/pcm_devices.c new file mode 100644 index 0000000000..913b59fb08 --- /dev/null +++ b/engines/sci/sfx/pcm_device/pcm_devices.c @@ -0,0 +1,73 @@ +/*************************************************************************** + pcmout.c Copyright (C) 2002 Solomon Peachy + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#include +#include + +#ifndef NO_PCMOUT +#ifdef SCUMMVM +extern sfx_pcm_device_t sfx_pcm_driver_scummvm; +#else // SCUMMVM +# ifdef HAVE_SDL +extern sfx_pcm_device_t sfx_pcm_driver_sdl; +# endif +# ifdef HAVE_ALSA +extern sfx_pcm_device_t sfx_pcm_driver_alsa; +# endif +# ifdef _DREAMCAST +extern sfx_pcm_device_t sfx_pcm_driver_dc; +# endif +#endif // SCUMMVM +#endif + +sfx_pcm_device_t *pcmout_drivers[] = { +#ifndef NO_PCMOUT +#ifdef SCUMMVM + &sfx_pcm_driver_scummvm, +#else // SCUMMVM +# ifdef HAVE_SDL + &sfx_pcm_driver_sdl, +# endif +# ifdef HAVE_ALSA + &sfx_pcm_driver_alsa, +# endif +# ifdef _DREAMCAST + &sfx_pcm_driver_dc, +# endif +#endif // SCUMMVM +#endif + NULL +}; + +sfx_pcm_device_t * +sfx_pcm_find_device(char *name) +{ + int retval = 0; + + if (!name) { /* Find default driver */ + return pcmout_drivers[0]; + } + + while (pcmout_drivers[retval] && + strcasecmp(name, pcmout_drivers[retval]->name)) + retval++; + + return pcmout_drivers[retval]; +} + diff --git a/engines/sci/sfx/pcm_device/scummvm.cpp b/engines/sci/sfx/pcm_device/scummvm.cpp new file mode 100644 index 0000000000..1b64dd40d3 --- /dev/null +++ b/engines/sci/sfx/pcm_device/scummvm.cpp @@ -0,0 +1,60 @@ +#include "sfx_time.h" +#include "sfx_pcm.h" +#include "engines/engine.h" +#include "sound/audiostream.h" +#include "sound/mixer.h" + + +static int pcmout_scummvm_framesize; +static Audio::AppendableAudioStream * pcmout_scummvm_audiostream; +static Audio::SoundHandle pcmout_scummvm_sound_handle; + + +static int pcmout_scummvm_init(sfx_pcm_device_t *self) { + int pcmout_scummvm_audiostream_flags = Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_STEREO; + +#ifdef SCUMM_LITTLE_ENDIAN + pcmout_scummvm_audiostream_flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN; +#endif + + self->buf_size = 2048 << 1; + self->conf.rate = g_engine->_mixer->getOutputRate(); + self->conf.stereo = SFX_PCM_STEREO_LR; + self->conf.format = SFX_PCM_FORMAT_S16_NATIVE; + pcmout_scummvm_framesize = SFX_PCM_FRAME_SIZE(self->conf); + + pcmout_scummvm_audiostream = Audio::makeAppendableAudioStream(self->conf.rate, pcmout_scummvm_audiostream_flags); + ::g_engine->_mixer->playInputStream(Audio::Mixer::kSFXSoundType, &pcmout_scummvm_sound_handle, pcmout_scummvm_audiostream); + + return SFX_OK; +} + +static void pcmout_scummvm_exit(sfx_pcm_device_t *self) { +} + +static int pcmout_scummvm_output(sfx_pcm_device_t *self, byte *buf, int count, + sfx_timestamp_t *timestamp) { + + byte *__buf = new byte[count * pcmout_scummvm_framesize]; + + memcpy(__buf, buf, count * pcmout_scummvm_framesize); + + pcmout_scummvm_audiostream->queueBuffer(__buf, count * pcmout_scummvm_framesize); + + return SFX_OK; +} + + +sfx_pcm_device_t sfx_pcm_driver_scummvm = { + "ScummVM", + "0.1", + &pcmout_scummvm_init, + &pcmout_scummvm_exit, + NULL, + &pcmout_scummvm_output, + NULL, + {0, 0, 0}, + 0, + NULL, + NULL + }; diff --git a/engines/sci/sfx/pcm_device/sdl.c b/engines/sci/sfx/pcm_device/sdl.c new file mode 100644 index 0000000000..bd125835cb --- /dev/null +++ b/engines/sci/sfx/pcm_device/sdl.c @@ -0,0 +1,272 @@ +/*************************************************************************** + sdl.c Copyright (C) 2002 Solomon Peachy, Claudio Matsuoka, + 2003,04 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#include +#include "audiobuf.h" + +#ifdef HAVE_SDL + +#if !defined(_MSC_VER) +# include +#endif + +#include +#undef HAVE_ICONV +#undef HAVE_ICONV_H +#undef HAVE_ALLOCA_H + +#include + +#define DELTA_TIME_LIMIT 10000 /* Report errors above this delta time */ + +static sfx_audio_buf_t audio_buffer; +static void (*sdl_sfx_timer_callback)(void *data); +static void *sdl_sfx_timer_data; +static int frame_size; +static int buf_size; +static int rate; +static long last_callback_secs, last_callback_usecs; +static int last_callback_len; + +static sfx_timestamp_t +pcmout_sdl_output_timestamp(sfx_pcm_device_t *self) +{ + /* Number of frames enqueued in the output device: */ + int delta = (buf_size - last_callback_len) / frame_size + /* Number of frames enqueued in the internal audio buffer: */ + + audio_buffer.frames_nr; + + return sfx_timestamp_add(sfx_new_timestamp(last_callback_secs, + last_callback_usecs, + rate), + delta); +} + +FILE *fil = NULL; + +static void +timer_sdl_internal_callback(void *userdata, byte *dest, int len) +{ + sci_gettime(&last_callback_secs, &last_callback_usecs); + last_callback_len = len; + + if (sdl_sfx_timer_callback) + sdl_sfx_timer_callback(sdl_sfx_timer_data); + +#if 0 + if (!sfx_audbuf_read_timestamp(&audio_buffer, &ts)) { + int delta = (buf_size - len) / frame_size; + sfx_timestamp_t real_ts; + long deltatime; + long sec2, usec2; + sci_gettime(&sec2, &usec2); + + real_ts = sfx_timestamp_add(sfx_new_timestamp(sec, usec, rate), delta); + + deltatime = sfx_timestamp_usecs_diff(ts, real_ts); + + fprintf(stderr, "[SDL] Frames requested: %d Playing %ld too late. Needed %ldus for computations.\n", + len / frame_size, deltatime, + (usec2-usec) + (sec2-sec)*1000000); + + if (abs(deltatime) > DELTA_TIME_LIMIT) + sciprintf("[SND:SDL] Very high delta time for PCM playback: %ld too late (%d frames in the future)\n", + deltatime, sfx_timestamp_frame_diff(sfx_new_timestamp(sec, usec, rate), ts)); + +#if 0 + if (deltatime < 0) { + /* Read and discard frames, explicitly underrunning */ + int max_read = len / frame_size; + int frames_to_kill = sfx_timestamp_frame_diff(real_ts, ts); + + while (frames_to_kill) { + int d = frames_to_kill > max_read? max_read : frames_to_kill; + sfx_audbuf_read(&audio_buffer, dest, d); + frames_to_kill -= d; + } + } +#endif + } +#endif + sfx_audbuf_read(&audio_buffer, dest, len / frame_size); + +#if 0 + if (!fil) { + fil = fopen("/tmp/sdl.log", "w"); + } + { + int i; + int end = len / frame_size; + gint16 *d = dest; + fprintf(fil, "Writing %d/%d\n", len, frame_size); + for (i = 0; i < end; i++) { + fprintf(fil, "\t%d\t%d\n", d[0], d[1]); + d += 2; + } + } +#endif +} + + +static int +pcmout_sdl_init(sfx_pcm_device_t *self) +{ + SDL_AudioSpec a; + + if (SDL_Init(SDL_INIT_AUDIO|SDL_INIT_NOPARACHUTE) != 0) { + fprintf (stderr, "[SND:SDL] Error while initialising: %s\n", SDL_GetError()); + return -1; + } + + a.freq = 44100; /* FIXME */ +#ifdef WORDS_BIGENDIAN + a.format = AUDIO_S16MSB; /* FIXME */ +#else + a.format = AUDIO_S16LSB; /* FIXME */ +#endif + a.channels = 2; /* FIXME */ + a.samples = 2048; /* FIXME */ + a.callback = timer_sdl_internal_callback; + a.userdata = NULL; + + if (SDL_OpenAudio (&a, NULL) < 0) { + fprintf (stderr, "[SND:SDL] Error while opening audio: %s\n", SDL_GetError()); + return SFX_ERROR; + } + + buf_size = a.samples; + rate = a.freq; + + self->buf_size = a.samples << 1; /* Looks like they're using double size */ + self->conf.rate = a.freq; + self->conf.stereo = a.channels > 1; + self->conf.format = SFX_PCM_FORMAT_S16_NATIVE; + + frame_size = SFX_PCM_FRAME_SIZE(self->conf); + + sfx_audbuf_init(&audio_buffer, self->conf); + SDL_PauseAudio (0); + + return SFX_OK; +} + +int +pcmout_sdl_output(sfx_pcm_device_t *self, byte *buf, + int count, sfx_timestamp_t *ts) +{ + if (ts) + sfx_audbuf_write_timestamp(&audio_buffer, *ts); + sfx_audbuf_write(&audio_buffer, buf, count); + return SFX_OK; +} + + +static int +pcmout_sdl_set_option(sfx_pcm_device_t *self, char *name, char *value) +{ + return SFX_ERROR; /* Option not supported */ +} + +static void +pcmout_sdl_exit(sfx_pcm_device_t *self) +{ + SDL_PauseAudio (1); + SDL_CloseAudio(); + sfx_audbuf_free(&audio_buffer); + + SDL_QuitSubSystem(SDL_INIT_AUDIO); + + if (!SDL_WasInit(SDL_INIT_EVERYTHING)) { + sciprintf("[SND:SDL] No active SDL subsystems found.. shutting down SDL\n"); + SDL_Quit(); + } +} + + +static int +timer_sdl_set_option(char *name, char *value) +{ + return SFX_ERROR; +} + + +static int +timer_sdl_init(void (*callback)(void *data), void *data) +{ + sdl_sfx_timer_callback = callback; + sdl_sfx_timer_data = data; + + + return SFX_OK; +} + +static int +timer_sdl_stop(void) +{ + sdl_sfx_timer_callback = NULL; + + return SFX_OK; +} + +static int +timer_sdl_block(void) +{ + SDL_LockAudio(); + + return SFX_OK; +} + +static int +timer_sdl_unblock(void) +{ + SDL_UnlockAudio(); + + return SFX_OK; +} + +#define SDL_PCM_VERSION "0.1" + +sfx_timer_t pcmout_sdl_timer = { + "sdl-pcm-timer", + SDL_PCM_VERSION, + 0, + 0, + timer_sdl_set_option, + timer_sdl_init, + timer_sdl_stop, + timer_sdl_block, + timer_sdl_unblock +}; + +sfx_pcm_device_t sfx_pcm_driver_sdl = { + "sdl", + SDL_PCM_VERSION, + pcmout_sdl_init, + pcmout_sdl_exit, + pcmout_sdl_set_option, + pcmout_sdl_output, + pcmout_sdl_output_timestamp, + {0, 0, 0}, + 0, + &pcmout_sdl_timer, + NULL +}; + +#endif diff --git a/engines/sci/sfx/player/Makefile.am b/engines/sci/sfx/player/Makefile.am new file mode 100644 index 0000000000..34ae8e8427 --- /dev/null +++ b/engines/sci/sfx/player/Makefile.am @@ -0,0 +1,3 @@ +noinst_LIBRARIES = libsciplayer.a +INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@ +libsciplayer_a_SOURCES = players.c realtime.c polled.c diff --git a/engines/sci/sfx/player/players.c b/engines/sci/sfx/player/players.c new file mode 100644 index 0000000000..d6d7cbaf17 --- /dev/null +++ b/engines/sci/sfx/player/players.c @@ -0,0 +1,54 @@ +/*************************************************************************** + players.c Copyright (C) 2002..04 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include + +extern sfx_player_t sfx_player_realtime; +extern sfx_player_t sfx_player_polled; + +sfx_player_t *sfx_players[] = { + &sfx_player_polled, + &sfx_player_realtime, + NULL +}; + +sfx_player_t * +sfx_find_player(char *name) +{ + if (!name) { + /* Implement platform policy here */ + + return sfx_players[0]; + } else { + int n = 0; + while (sfx_players[n] && + strcasecmp(sfx_players[n]->name, name)) + ++n; + + return sfx_players[n]; + } +} diff --git a/engines/sci/sfx/player/polled.c b/engines/sci/sfx/player/polled.c new file mode 100644 index 0000000000..59eeceaac2 --- /dev/null +++ b/engines/sci/sfx/player/polled.c @@ -0,0 +1,335 @@ +/*************************************************************************** + polled.c Copyright (C) 2004 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Polled player, mostly for PCM-based thingies (which _can_ poll, after all) */ + +#include +#include "../softseq.h" +#include "../mixer.h" + +static song_iterator_t *play_it; +static int play_paused = 0; +static sfx_softseq_t *seq; +static int volume = 100; +static sfx_timestamp_t new_timestamp; +static int new_song = 0; + +/* The time counter is used to determine how close to the end of a tick we are. +** For each frame played, it is decreased by 60. */ +#define TIME_INC 60 +static int time_counter = 0; + +static void +pp_tell_synth(int buf_nr, byte *buf) +{ + seq->handle_command(seq, buf[0], buf_nr-1, buf+1); +} + + +/*----------------------*/ +/* Mixer implementation */ +/*----------------------*/ +int +ppf_poll(sfx_pcm_feed_t *self, byte *dest, int size) +{ + int written = 0; + byte buf[4]; + int buf_nr; + + if (!play_it) + return 0; + + if (play_paused) + return 0; + + while (written < size) { + int can_play; + int do_play; + + while (time_counter <= TIME_INC) { + int next_stat = songit_next(&play_it, + &(buf[0]), &buf_nr, + IT_READER_MASK_ALL + | IT_READER_MAY_FREE + | IT_READER_MAY_CLEAN); + + switch (next_stat) { + case SI_PCM: + sfx_play_iterator_pcm(play_it, 0); + break; + + case SI_FINISHED: + songit_free(play_it); + play_it = NULL; + return written; /* We're done... */ + + case SI_IGNORE: + case SI_LOOP: + case SI_RELATIVE_CUE: + case SI_ABSOLUTE_CUE: + break; /* Boooring... .*/ + + case 0: /* MIDI command */ + + seq->handle_command(seq, buf[0], buf_nr - 1, buf+1); + break; + + default: + time_counter += next_stat * seq->pcm_conf.rate; + } + } + + can_play = time_counter / TIME_INC; + do_play = (can_play > (size - written))? (size - written) : can_play; + + time_counter -= do_play * TIME_INC; + + seq->poll(seq, dest + written * self->frame_size, do_play); + written += do_play; + } + + return size; /* Apparently, we wrote all that was requested */ +} + +void +ppf_destroy(sfx_pcm_feed_t *self) +{ + /* no-op */ +} + +int +ppf_get_timestamp(sfx_pcm_feed_t *self, sfx_timestamp_t *timestamp) +{ + if (!new_song) + return PCM_FEED_IDLE; + + /* Otherwise, we have a timestamp: */ + + *timestamp = new_timestamp; + new_song = 0; + return PCM_FEED_TIMESTAMP; +} + +extern sfx_player_t sfx_player_polled; +static +sfx_pcm_feed_t pcmfeed = { + ppf_poll, + ppf_destroy, + ppf_get_timestamp, + NULL, + {0, 0, 0}, + "polled-player-feed", + 0, + 0 /* filled in by the mixer */ +}; + +/*=======================*/ +/* Player implementation */ +/*=======================*/ + + +/*--------------------*/ +/* API implementation */ +/*--------------------*/ + +static void +pp_timer_callback(void) +{ + /* Hey, we're polled anyway ;-) */ +} + +static int +pp_set_option(char *name, char *value) +{ + return SFX_ERROR; +} + +static int +pp_init(resource_mgr_t *resmgr, int expected_latency) +{ + resource_t *res = NULL, *res2 = NULL; + int fd; + + if (!mixer) + return SFX_ERROR; + + /* FIXME Temporary hack to detect Amiga games. */ + fd = sci_open("bank.001", O_RDONLY); + + if (fd == SCI_INVALID_FD) + seq = sfx_find_softseq(NULL); + else { + close(fd); + seq = sfx_find_softseq("amiga"); + } + + if (!seq) { + sciprintf("[sfx:seq:polled] Initialisation failed: Could not find software sequencer\n"); + return SFX_ERROR; + } + + if (seq->patch_nr != SFX_SEQ_PATCHFILE_NONE) { + res = scir_find_resource(resmgr, sci_patch, seq->patch_nr, 0); + } + + if (seq->patch2_nr != SFX_SEQ_PATCHFILE_NONE) { + res2 = scir_find_resource(resmgr, sci_patch, seq->patch2_nr, 0); + } + + if (seq->init(seq, + (res)? res->data : NULL, + (res)? res->size : 0, + (res2)? res2->data : NULL, + (res2)? res2->size : 0)) { + sciprintf("[sfx:seq:polled] Initialisation failed: Sequencer '%s', v%s failed to initialise\n", + seq->name, seq->version); + return SFX_ERROR; + } + + pcmfeed.conf = seq->pcm_conf; + + seq->set_volume(seq, volume); + mixer->subscribe(mixer, &pcmfeed); + + sfx_player_polled.polyphony = seq->polyphony; + return SFX_OK; +} + +static int +pp_add_iterator(song_iterator_t *it, GTimeVal start_time) +{ + song_iterator_t *old = play_it; + + SIMSG_SEND(it, SIMSG_SET_PLAYMASK(seq->playmask)); + SIMSG_SEND(it, SIMSG_SET_RHYTHM(seq->play_rhythm)); + + if (play_it == NULL) + seq->allstop(seq); + + play_it = sfx_iterator_combine(play_it, it); + + seq->set_volume(seq, volume); + + /* The check must happen HERE, and not at the beginning of the + function, to avoid a race condition with the mixer. */ + if (old == NULL) { + new_timestamp = sfx_new_timestamp(start_time.tv_sec, + start_time.tv_usec, + seq->pcm_conf.rate); + /* ASAP otherwise */ + time_counter = 0; + new_song = 1; + } + + return SFX_OK; +} + +static int +pp_fade_out(void) +{ + fprintf(stderr, __FILE__": Attempt to fade out- not implemented yet\n"); + return SFX_ERROR; +} + +static int +pp_stop(void) +{ + song_iterator_t *it = play_it; + + play_it = NULL; +fprintf(stderr, "[play] Now stopping it %p\n", (void *)it); + if (it) + songit_free(it); + + seq->allstop(seq); + + return SFX_OK; +} + +static int +pp_send_iterator_message(song_iterator_message_t msg) +{ + if (!play_it) + return SFX_ERROR; + + songit_handle_message(&play_it, msg); + return SFX_OK; +} + +static int +pp_pause(void) +{ + play_paused = 1; + seq->set_volume(seq, 0); + + return SFX_OK; +} + +static int +pp_resume(void) +{ + if (!play_it) + { + play_paused = 0; + return SFX_OK; /* Nothing to resume */ + } + + if (play_paused) + new_song = 1; /* Fake starting a new song, re-using the old + ** time stamp (now long in the past) to indicate + ** resuming ASAP */ + + play_paused = 0; + seq->set_volume(seq, volume); + return SFX_OK; +} + +static int +pp_exit(void) +{ + seq->exit(seq); + songit_free(play_it); + play_it = NULL; + + return SFX_OK; +} + +sfx_player_t sfx_player_polled = { + "polled", + "0.1", + &pp_set_option, + &pp_init, + &pp_add_iterator, + &pp_fade_out, + &pp_stop, + &pp_send_iterator_message, + &pp_pause, + &pp_resume, + &pp_exit, + &pp_timer_callback, + &pp_tell_synth, + 0 /* polyphony */ +}; diff --git a/engines/sci/sfx/player/realtime.c b/engines/sci/sfx/player/realtime.c new file mode 100644 index 0000000000..752b0b816b --- /dev/null +++ b/engines/sci/sfx/player/realtime.c @@ -0,0 +1,328 @@ +/*************************************************************************** + realtime.c Copyright (C) 2002..04 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* OK... 'realtime' may be a little too euphemistic, as this one just +** prays for some reasonable amount of soft real-time, but it's close +** enough, I guess. */ + +#include +#include "../sequencer.h" + +static sfx_sequencer_t *seq; + +extern sfx_player_t sfx_player_realtime; + +/* Playing mechanism */ + +static inline GTimeVal +current_time(void) +{ + GTimeVal tv; + sci_get_current_time(&tv); + return tv; +} + +static inline GTimeVal +add_time_delta(GTimeVal tv, long delta) +{ + int sec_d; + + tv.tv_usec += delta; + sec_d = tv.tv_usec / 1000000; + tv.tv_usec -= sec_d * 1000000; + + tv.tv_sec += sec_d; + + return tv; +} + +static inline long +delta_time(GTimeVal comp_tv, GTimeVal base) +{ + GTimeVal tv; + sci_get_current_time(&tv); + return (comp_tv.tv_sec - tv.tv_sec) * 1000000 + + (comp_tv.tv_usec - tv.tv_usec); +} + +static song_iterator_t *play_it = NULL; +static GTimeVal play_last_time; +static GTimeVal play_pause_started; /* Beginning of the last pause */ +static GTimeVal play_pause_counter; /* Last point in time to mark a + ** play position augmentation */ +static int play_paused = 0; +static int play_it_done = 0; +static int play_writeahead = 0; +static int play_moredelay = 0; + +static void +play_song(song_iterator_t *it, GTimeVal *wakeup_time, int writeahead_time) +{ + unsigned char buf[8]; + int result; + + if (play_paused) { + GTimeVal ct; + sci_get_current_time(&ct); + + *wakeup_time = + add_time_delta(*wakeup_time, delta_time(play_pause_counter, ct)); + play_pause_counter = ct; + } else + /* Not paused: */ + while (play_it && delta_time(*wakeup_time, current_time()) + < writeahead_time) { + int delay; + + switch ((delay = songit_next(&(play_it), + &(buf[0]), &result, + IT_READER_MASK_ALL + | IT_READER_MAY_FREE + | IT_READER_MAY_CLEAN))) { + + case SI_FINISHED: + play_it_done = 1; + return; + + case SI_IGNORE: + case SI_LOOP: + case SI_RELATIVE_CUE: + case SI_ABSOLUTE_CUE: + break; + + case SI_PCM: + sfx_play_iterator_pcm(play_it, 0); + break; + + case 0: + seq->event(buf[0], result-1, buf+1); + + break; + + default: + play_moredelay = delay - 1; + *wakeup_time = song_next_wakeup_time(wakeup_time, delay); + if (seq->delay) + seq->delay(delay); + } + } +} + +static void +rt_tell_synth(int buf_nr, byte *buf) +{ + seq->event(buf[0], buf_nr-1, buf+1); +} + +static void +rt_timer_callback(void) +{ + if (play_it && !play_it_done) { + if (!play_moredelay) { + long delta = delta_time(play_last_time, current_time()); + + if (delta < 0) { + play_writeahead -= (int)((double)delta * 1.2); /* Adjust upwards */ + } else if (delta > 15000) { + play_writeahead -= 2500; /* Adjust downwards */ + } + } else --play_moredelay; + + if (play_writeahead < seq->min_write_ahead_ms) + play_writeahead = seq->min_write_ahead_ms; + + play_song(play_it, &play_last_time, play_writeahead); + } +} + +static resource_t * +find_patch(resource_mgr_t *resmgr, const char *seq, int patchfile) +{ + resource_t *res = NULL; + + if (patchfile != SFX_SEQ_PATCHFILE_NONE) { + res = scir_find_resource(resmgr, sci_patch, patchfile, 0); + if (!res) { + fprintf(stderr, "[SFX] " __FILE__": patch.%03d requested by sequencer (%s), but not found\n", + patchfile, seq); + } + } + + return res; +} + +/* API implementation */ + +static int +rt_set_option(char *name, char *value) +{ + return SFX_ERROR; +} + +static int +rt_init(resource_mgr_t *resmgr, int expected_latency) +{ + resource_t *res = NULL, *res2 = NULL; + void *seq_dev = NULL; + GTimeVal foo = {0,0}; + + seq = sfx_find_sequencer(NULL); + + if (!seq) { + fprintf(stderr, "[SFX] " __FILE__": Could not find sequencer\n"); + return SFX_ERROR; + } + + sfx_player_realtime.polyphony = seq->polyphony; + + res = find_patch(resmgr, seq->name, seq->patchfile); + res2 = find_patch(resmgr, seq->name, seq->patchfile2); + + if (seq->device) + seq_dev = sfx_find_device(seq->device, NULL); + + if (seq->open(res? res->size : 0, + res? res->data : NULL, + res2? res2->size : 0, + res2? res2->data : NULL, + seq_dev)) { + fprintf(stderr, "[SFX] " __FILE__": Sequencer failed to initialize\n"); + return SFX_ERROR; + } + + play_writeahead = expected_latency; + if (play_writeahead < seq->min_write_ahead_ms) + play_writeahead = seq->min_write_ahead_ms; + + play_writeahead *= 1000; /* microseconds */ + + if (seq->reset_timer) + seq->reset_timer(foo); + + return SFX_OK; +} + +static int +rt_add_iterator(song_iterator_t *it, GTimeVal start_time) +{ + if (seq->reset_timer) /* Restart timer counting if possible */ + seq->reset_timer(start_time); + + SIMSG_SEND(it, SIMSG_SET_PLAYMASK(seq->playmask)); + SIMSG_SEND(it, SIMSG_SET_RHYTHM(seq->play_rhythm)); + + play_last_time = start_time; + play_it = sfx_iterator_combine(play_it, it); + play_it_done = 0; + play_moredelay = 0; + + return SFX_OK; +} + +static int +rt_fade_out(void) +{ + fprintf(stderr, __FILE__": Attempt to fade out- not implemented yet\n"); + return SFX_ERROR; +} + +static int +rt_stop(void) +{ + song_iterator_t *it = play_it; + + play_it = NULL; + + if (it) + songit_free(it); + if (seq && seq->allstop) + seq->allstop(); + + return SFX_OK; +} + +static int +rt_send_iterator_message(song_iterator_message_t msg) +{ + if (!play_it) + return SFX_ERROR; + + songit_handle_message(&play_it, msg); + return SFX_OK; +} + +static int +rt_pause(void) +{ + sci_get_current_time(&play_pause_started); + /* Also, indicate that we haven't modified the time counter + ** yet */ + play_pause_counter = play_pause_started; + + play_paused = 1; + if (!seq->allstop) { + sciprintf("[SFX] Cannot suspend sequencer, sound will continue for a bit\n"); + return SFX_OK; + } else + return seq->allstop(); +} + +static int +rt_resume(void) +{ + play_paused = 0; + return SFX_OK; +} + +static int +rt_exit(void) +{ + int retval = SFX_OK; + + if(seq->close()) { + fprintf(stderr, "[SFX] Sequencer reported error on close\n"); + retval = SFX_ERROR; + } + + return retval; +} + +sfx_player_t sfx_player_realtime = { + "realtime", + "0.1", + &rt_set_option, + &rt_init, + &rt_add_iterator, + &rt_fade_out, + &rt_stop, + &rt_send_iterator_message, + &rt_pause, + &rt_resume, + &rt_exit, + &rt_timer_callback, + &rt_tell_synth, + 0 /* polyphony */ +}; diff --git a/engines/sci/sfx/seq/Makefile.am b/engines/sci/sfx/seq/Makefile.am new file mode 100644 index 0000000000..74fb4fda3f --- /dev/null +++ b/engines/sci/sfx/seq/Makefile.am @@ -0,0 +1,5 @@ +noinst_LIBRARIES = libsciseq.a +INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@ +EXTRA_DIST = instrument-map.h +libsciseq_a_SOURCES = sequencers.c oss-adlib.c mt32.c gm.c instrument-map.c \ + map-mt32-to-gm.c diff --git a/engines/sci/sfx/seq/gm.c b/engines/sci/sfx/seq/gm.c new file mode 100644 index 0000000000..4889e76ea8 --- /dev/null +++ b/engines/sci/sfx/seq/gm.c @@ -0,0 +1,185 @@ +/*************************************************************************** + Copyright (C) 2008 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantability, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +# include +#endif +#include "../sequencer.h" +#include "../device.h" +#include "instrument-map.h" +#include + +static midi_writer_t *writer = NULL; + +static int +midi_gm_open(int patch_len, byte *data, int patch2_len, byte *data2, void *device) +{ + sfx_instrument_map_t *instrument_map = sfx_instrument_map_load_sci(data, patch_len); + + if (!instrument_map) { + fprintf(stderr, "[GM] No GM instrument map found, trying MT-32 instrument map..\n"); + instrument_map = sfx_instrument_map_mt32_to_gm(data2, patch2_len); + } + + writer = sfx_mapped_writer((midi_writer_t *) device, instrument_map); + + if (!writer) + return SFX_ERROR; + + if (writer->reset_timer) + writer->reset_timer(writer); + + return SFX_OK; +} + +static int +midi_gm_close(void) +{ + return SFX_OK; +} + +static int +midi_gm_event(byte command, int argc, byte *argv) +{ + byte data[4]; + + assert (argc < 4); + data[0] = command; + memcpy(data + 1, argv, argc); + + writer->write(writer, data, argc + 1); + + return SFX_OK; +} + +static int +midi_gm_delay(int ticks) +{ + writer->delay(writer, ticks); + + return SFX_OK; +} + +static int +midi_gm_reset_timer(GTimeVal ts) +{ + writer->reset_timer(writer); + + return SFX_OK; +} + +#define MIDI_MASTER_VOLUME_LEN 8 + +static int +midi_gm_volume(guint8 volume) +{ + byte data[MIDI_MASTER_VOLUME_LEN] = { + 0xf0, + 0x7f, + 0x7f, + 0x04, + 0x01, + volume, + volume, + 0xf7}; + + writer->write(writer, data, MIDI_MASTER_VOLUME_LEN); + if (writer->flush) + writer->flush(writer); + + return SFX_OK; +} + +static int +midi_gm_allstop(void) +{ + byte data[3] = { 0xb0, + 0x78, /* all sound off */ + 0 }; + int i; + + /* All sound off on all channels */ + for (i = 0; i < 16; i++) { + data[0] = 0xb0 | i; + writer->write(writer, data, 3); + } + if (writer->flush) + writer->flush(writer); + + return SFX_OK; +} + +static int +midi_gm_reverb(int reverb) +{ + byte data[3] = { 0xb0, + 91, /* set reverb */ + reverb }; + int i; + + /* Set reverb on all channels */ + for (i = 0; i < 16; i++) + if (i != 9) { + data[0] = 0xb0 | i; + writer->write(writer, data, 3); + } + if (writer->flush) + writer->flush(writer); + + return SFX_OK; +} + +static int +midi_gm_set_option(char *x, char *y) +{ + return SFX_ERROR; +} + +sfx_sequencer_t sfx_sequencer_gm = { + "General MIDI", + "0.1", + SFX_DEVICE_MIDI, + &midi_gm_set_option, + &midi_gm_open, + &midi_gm_close, + &midi_gm_event, + &midi_gm_delay, + &midi_gm_reset_timer, + &midi_gm_allstop, + &midi_gm_volume, + &midi_gm_reverb, + 004, /* patch.004 */ + 001, /* patch.001 */ + 0x01, /* playflag */ + 1, /* do play rhythm */ + 64, /* max polyphony */ + 0 /* no write-ahead needed inherently */ +}; diff --git a/engines/sci/sfx/seq/instrument-map.c b/engines/sci/sfx/seq/instrument-map.c new file mode 100644 index 0000000000..0d829a0582 --- /dev/null +++ b/engines/sci/sfx/seq/instrument-map.c @@ -0,0 +1,539 @@ +/*************************************************************************** + Copyright (C) 2008 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantability, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include "sci_midi.h" +#include "sci_memory.h" +#include "instrument-map.h" +#include "sfx_engine.h" + +sfx_instrument_map_t * +sfx_instrument_map_new(int velocity_maps_nr) +{ + sfx_instrument_map_t *map = (sfx_instrument_map_t *)sci_malloc(sizeof (sfx_instrument_map_t)); + int i; + + map->initialisation_block_size = 0; + map->initialisation_block = NULL; + + map->velocity_maps_nr = velocity_maps_nr; + map->percussion_velocity_map_index = SFX_NO_VELOCITY_MAP; + + if (velocity_maps_nr == 0) + map->velocity_map = NULL; /* Yes, this complicates control flow needlessly, but it avoids some of the pointless + ** warnings that certain memory tools seem to find appropriate. */ + else { + map->velocity_map = (byte **)sci_malloc(sizeof (byte *) * velocity_maps_nr); + for (i = 0; i < velocity_maps_nr; ++i) + map->velocity_map[i] = (byte *)sci_malloc(SFX_VELOCITIES_NR); + } + for (i = 0; i < SFX_INSTRUMENTS_NR; ++i) + map->velocity_map_index[i] = SFX_NO_VELOCITY_MAP; + + map->percussion_volume_adjust = 0; + for (i = 0; i < SFX_RHYTHM_NR; ++i) + map->percussion_map[i] = i; + + + for (i = 0; i < SFX_INSTRUMENTS_NR; ++i) { + map->patch_map[i].patch = i; + map->patch_key_shift[i] = 0; + map->patch_volume_adjust[i] = 0; + } + + return map; +} + +void +sfx_instrument_map_free(sfx_instrument_map_t *map) +{ + if (!map) + return; + + if (map->velocity_map) { + int i; + for (i = 0; i < map->velocity_maps_nr; i++) + sci_free(map->velocity_map[i]); + sci_free(map->velocity_map); + map->velocity_map = NULL; + } + + if (map->initialisation_block) { + sci_free(map->initialisation_block); + map->initialisation_block = NULL; + } + + sci_free(map); +} + +#define PATCH_MAP_OFFSET 0x0000 +#define PATCH_KEY_SHIFT_OFFSET 0x0080 +#define PATCH_VOLUME_ADJUST_OFFSET 0x0100 +#define PATCH_PERCUSSION_MAP_OFFSET 0x0180 +#define PATCH_PERCUSSION_VOLUME_ADJUST 0x0200 +#define PATCH_VELOCITY_MAP_INDEX 0x0201 +#define PATCH_VELOCITY_MAP(i) (0x0281 + (0x80 * i)) +#define PATCH_INIT_DATA_SIZE_LE 0x0481 +#define PATCH_INIT_DATA 0x0483 + +#define PATCH_INSTRUMENT_MAPS_NR 4 + +#define PATCH_MIN_SIZE PATCH_INIT_DATA + + +static int +patch001_type0_length(byte *data, size_t length) +{ + unsigned int pos = 492 + 246 * data[491]; + +/* printf("timbres %d (post = %04x)\n",data[491], pos);*/ + + if ((length >= (pos + 386)) && (data[pos] == 0xAB) && (data[pos + 1] == 0xCD)) + pos += 386; + +/* printf("pos = %04x (%02x %02x)\n", pos, data[pos], data[pos + 1]); */ + + if ((length >= (pos + 267)) && (data[pos] == 0xDC) && (data[pos + 1] == 0xBA)) + pos += 267; + +/* printf("pos = %04x %04x (%d)\n", pos, length, pos-length); */ + + + if (pos == length) + return 1; + return 0; +} + +static int +patch001_type1_length(byte *data, size_t length) +{ + if ((length >= 1155) && (((data[1154] << 8) + data[1153] + 1155) == length)) + return 1; + return 0; +} + +int +sfx_instrument_map_detect(byte *data, size_t length) +{ + /* length test */ + if (length < 1155) + return SFX_MAP_MT32; + if (length > 16889) + return SFX_MAP_MT32_GM; + if (patch001_type0_length(data, length) && + !patch001_type1_length(data, length)) + return SFX_MAP_MT32; + if (patch001_type1_length(data, length) && + !patch001_type0_length(data, length)) + return SFX_MAP_MT32_GM; + return SFX_MAP_UNKNOWN; +} + + +sfx_instrument_map_t * +sfx_instrument_map_load_sci(byte *data, size_t size) +{ + sfx_instrument_map_t * map; + int i, m; + + if (data == NULL) + return NULL; + + if (size < PATCH_MIN_SIZE) { + fprintf(stderr, "[instrument-map] Instrument map too small: %d of %d\n", (int) size, PATCH_MIN_SIZE); + return NULL; + } + + map = sfx_instrument_map_new(PATCH_INSTRUMENT_MAPS_NR); + + /* Set up MIDI intialisation data */ + map->initialisation_block_size = getInt16(data + PATCH_INIT_DATA_SIZE_LE); + if (map->initialisation_block_size) { + if (size < PATCH_MIN_SIZE + map->initialisation_block_size) { + fprintf(stderr, "[instrument-map] Instrument map too small for initialisation block: %d of %d\n", (int) size, PATCH_MIN_SIZE); + return NULL; + } + + if (size > PATCH_MIN_SIZE + map->initialisation_block_size) + fprintf(stderr, "[instrument-map] Instrument larger than required by initialisation block: %d of %d\n", (int) size, PATCH_MIN_SIZE); + + if (map->initialisation_block_size != 0) { + map->initialisation_block = (byte *)sci_malloc(map->initialisation_block_size); + memcpy(map->initialisation_block, data + PATCH_INIT_DATA, map->initialisation_block_size); + } + } + + /* Set up basic instrument info */ + for (i = 0; i < SFX_INSTRUMENTS_NR; i++) { + map->patch_map[i].patch = (char)data[PATCH_MAP_OFFSET + i]; + map->patch_key_shift[i] = (char)data[PATCH_KEY_SHIFT_OFFSET + i]; + map->patch_volume_adjust[i] = (char)data[PATCH_VOLUME_ADJUST_OFFSET + i]; + map->patch_bend_range[i] = SFX_UNMAPPED; + map->velocity_map_index[i] = data[PATCH_VELOCITY_MAP_INDEX + i]; + } + + /* Set up percussion maps */ + map->percussion_volume_adjust = data[PATCH_PERCUSSION_VOLUME_ADJUST]; + for (i = 0; i < SFX_RHYTHM_NR; i++) { + map->percussion_map[i] = data[PATCH_PERCUSSION_MAP_OFFSET + i]; + map->percussion_velocity_scale[i] = SFX_MAX_VELOCITY; + } + + /* Set up velocity maps */ + for (m = 0; m < PATCH_INSTRUMENT_MAPS_NR; m++) { + byte *velocity_map = map->velocity_map[m]; + for (i = 0; i < SFX_VELOCITIES_NR; i++) + velocity_map[i] = data[PATCH_VELOCITY_MAP(m) + i]; + } + + map->percussion_velocity_map_index = 0; + + return map; +} + + +/* Output with the instrument map */ +#define MIDI_CHANNELS_NR 0x10 + +typedef struct decorated_midi_writer { + MIDI_WRITER_BODY + + midi_writer_t *writer; + sfx_patch_map_t patches[MIDI_CHANNELS_NR]; + sfx_instrument_map_t *map; +} decorated_midi_writer_t; + + +static void +init_decorated(struct _midi_writer *self_) +{ + decorated_midi_writer_t *self = (decorated_midi_writer_t *) self_; + self->writer->init(self->writer); +} + +static void +set_option_decorated(struct _midi_writer *self_, char *name, char *value) +{ + decorated_midi_writer_t *self = (decorated_midi_writer_t *) self_; + self->writer->set_option(self->writer, name, value); +} + +static void +delay_decorated(struct _midi_writer *self_, int ticks) +{ + decorated_midi_writer_t *self = (decorated_midi_writer_t *) self_; + self->writer->delay(self->writer, ticks); +} + +static void +flush_decorated(struct _midi_writer *self_) +{ + decorated_midi_writer_t *self = (decorated_midi_writer_t *) self_; + if (self->writer->flush) + self->writer->flush(self->writer); +} + +static void +reset_timer_decorated(struct _midi_writer *self_) +{ + decorated_midi_writer_t *self = (decorated_midi_writer_t *) self_; + self->writer->reset_timer(self->writer); +} + + +static void +close_decorated(decorated_midi_writer_t *self) +{ + sfx_instrument_map_free(self->map); + self->map = NULL; + self->writer->close(self->writer); + sci_free(self->name); + self->name = NULL; + sci_free(self); +} + +#define BOUND_127(x) (((x) < 0)? 0 : (((x) > 0x7f)? 0x7f : (x))) + +static int +bound_hard_127(int i, char *descr) +{ + int r = BOUND_127(i); + if (r != i) + fprintf(stderr, "[instrument-map] Hard-clipping %02x to %02x in %s\n", i, r, descr); + return r; +} + +static int +set_bend_range(midi_writer_t *writer, int channel, int range) +{ + byte buf[3] = {0xb0, 0x65, 0x00}; + + buf[0] |= channel & 0xf; + if (writer->write(writer, buf, 3) != SFX_OK) + return SFX_ERROR; + + buf[1] = 0x64; + if (writer->write(writer, buf, 3) != SFX_OK) + return SFX_ERROR; + + buf[1] = 0x06; + buf[2] = BOUND_127(range); + if (writer->write(writer, buf, 3) != SFX_OK) + return SFX_ERROR; + + buf[1] = 0x26; + buf[2] = 0; + if (writer->write(writer, buf, 3) != SFX_OK) + return SFX_ERROR; + + return SFX_OK; +} + +static int +write_decorated(decorated_midi_writer_t *self, byte *buf, int len) +{ + sfx_instrument_map_t *map = self->map; + int op = *buf & 0xf0; + int chan = *buf & 0x0f; + int patch = self->patches[chan].patch; + int rhythm = self->patches[chan].rhythm; + + assert (len >= 1); + + if (op == 0xC0 && chan != MIDI_RHYTHM_CHANNEL) { /* Program change */ + int patch = bound_hard_127(buf[1], "program change"); + int instrument = map->patch_map[patch].patch; + int bend_range = map->patch_bend_range[patch]; + + self->patches[chan] = map->patch_map[patch]; + + if (instrument == SFX_UNMAPPED || instrument == SFX_MAPPED_TO_RHYTHM) + return SFX_OK; + + assert (len >= 2); + buf[1] = bound_hard_127(instrument, "patch lookup"); + + if (self->writer->write(self->writer, buf, len) != SFX_OK) + return SFX_ERROR; + + if (bend_range != SFX_UNMAPPED) + return set_bend_range(self->writer, chan, bend_range); + + return SFX_OK; + } + + if (chan == MIDI_RHYTHM_CHANNEL || patch == SFX_MAPPED_TO_RHYTHM) { + /* Rhythm channel handling */ + switch (op) { + case 0x80: + case 0x90: { /* Note off / note on */ + int velocity, instrument, velocity_map_index, velocity_scale; + + if (patch == SFX_MAPPED_TO_RHYTHM) { + buf[0] = (buf[0] & ~0x0f) | MIDI_RHYTHM_CHANNEL; + instrument = rhythm; + velocity_scale = SFX_MAX_VELOCITY; + } else { + int instrument_index = bound_hard_127(buf[1], "rhythm instrument index"); + instrument = map->percussion_map[instrument_index]; + velocity_scale = map->percussion_velocity_scale[instrument_index]; + } + + if (instrument == SFX_UNMAPPED) + return SFX_OK; + + assert (len >= 3); + + velocity = bound_hard_127(buf[2], "rhythm velocity"); + velocity_map_index = map->percussion_velocity_map_index; + + if (velocity_map_index != SFX_NO_VELOCITY_MAP) + velocity = BOUND_127(velocity + map->velocity_map[velocity_map_index][velocity]); + + velocity = BOUND_127(velocity * velocity_scale / SFX_MAX_VELOCITY); + + buf[1] = bound_hard_127(instrument, "rhythm instrument"); + buf[2] = velocity; + + break; + } + + case 0xB0: { /* Controller change */ + assert (len >= 3); + if (buf[1] == 0x7) /* Volume change */ + buf[2] = BOUND_127(buf[2] + map->percussion_volume_adjust); + break; + } + + default: break; + } + + } else { + /* Instrument channel handling */ + + if (patch == SFX_UNMAPPED) + return SFX_OK; + + switch (op) { + case 0x80: + case 0x90: { /* Note off / note on */ + int note = bound_hard_127(buf[1], "note"); + int velocity = bound_hard_127(buf[2], "velocity"); + int velocity_map_index = map->velocity_map_index[patch]; + assert (len >= 3); + + note += map->patch_key_shift[patch]; + /* Not the most efficient solutions, but the least error-prone */ + while (note < 0) + note += 12; + while (note > 0x7f) + note -= 12; + + if (velocity_map_index != SFX_NO_VELOCITY_MAP) + velocity = BOUND_127(velocity + map->velocity_map[velocity_map_index][velocity]); + + buf[1] = note; + buf[2] = velocity; + break; + } + + case 0xB0: /* Controller change */ + assert (len >= 3); + if (buf[1] == 0x7) /* Volume change */ + buf[2] = BOUND_127(buf[2] + map->patch_volume_adjust[patch]); + break; + + default: break; + } + } + + return self->writer->write(self->writer, buf, len); +} + +#define MIDI_BYTES_PER_SECOND 3250 /* This seems to be the minimum guarantee by the standard */ +#define MAX_PER_TICK (MIDI_BYTES_PER_SECOND / 60) /* After this, we ought to issue one tick of pause */ + +static void +init(midi_writer_t *writer, byte *data, size_t len) +{ + int offset = 0; + byte status = 0; + + /* Send init data as separate MIDI commands */ + while (offset < len) { + int args; + byte op = data[offset]; + byte msg[3]; + int i; + + if (op == 0xf0) { + int msg_len; + byte *find = (byte *) memchr(data + offset, 0xf7, len - offset); + + if (!find) { + fprintf(stderr, "[instrument-map] Failed to find end of sysex message\n"); + return; + } + + msg_len = find - data - offset + 1; + writer->write(writer, data + offset, msg_len); + + /* Wait at least 40ms after sysex */ + writer->delay(writer, 3); + offset += msg_len; + continue; + } + + if (op < 0x80) + op = status; + else { + status = op; + offset++; + } + + msg[0] = op; + + switch (op & 0xf0) { + case 0xc0: + case 0xd0: + args = 1; + break; + default: + args = 2; + } + + if (args > len - offset) { + fprintf(stderr, "[instrument-map] Insufficient bytes remaining for MIDI command %02x\n", op); + return; + } + + for (i = 0; i < args; i++) + msg[i + 1] = data[offset + i]; + + writer->write(writer, msg, args + 1); + offset += args; + + if (writer->flush) + writer->flush(writer); + } +} + +#define NAME_SUFFIX "+instruments" + +midi_writer_t * +sfx_mapped_writer(midi_writer_t *writer, sfx_instrument_map_t *map) +{ + int i; + decorated_midi_writer_t *retval; + + if (map == NULL) + return writer; + + retval = (decorated_midi_writer_t *)sci_malloc(sizeof(decorated_midi_writer_t)); + retval->writer = writer; + retval->name = (char *)sci_malloc(strlen(writer->name) + strlen(NAME_SUFFIX) + 1); + strcpy(retval->name, writer->name); + strcat(retval->name, NAME_SUFFIX); + + retval->init = (int (*)(midi_writer_t *)) init_decorated; + retval->set_option = (int (*)(midi_writer_t *, char *, char *)) set_option_decorated; + retval->write = (int (*)(midi_writer_t *, byte *, int)) write_decorated; + retval->delay = (void (*)(midi_writer_t *, int)) delay_decorated; + retval->flush = (void (*)(midi_writer_t *)) flush_decorated; + retval->reset_timer = (void (*)(midi_writer_t *)) reset_timer_decorated; + retval->close = (void (*)(midi_writer_t *)) close_decorated; + + retval->map = map; + + init(writer, map->initialisation_block, map->initialisation_block_size); + + for (i = 0; i < MIDI_CHANNELS_NR; i++) + retval->patches[i].patch = SFX_UNMAPPED; + + return (midi_writer_t *) retval; +} + diff --git a/engines/sci/sfx/seq/instrument-map.h b/engines/sci/sfx/seq/instrument-map.h new file mode 100644 index 0000000000..85f654d60b --- /dev/null +++ b/engines/sci/sfx/seq/instrument-map.h @@ -0,0 +1,132 @@ +/*************************************************************************** + Copyright (C) 2008 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantability, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +/* Implementation of SCI instrument maps for GM and MT-32. */ + +#ifndef SCI_INSTRUMENT_MAP_ +#define SCI_INSTRUMENT_MAP_ + +#include +#include "resource.h" +#include "../device.h" + +#define SFX_INSTRUMENTS_NR 0x80 +#define SFX_RHYTHM_NR 0x80 +#define SFX_VELOCITIES_NR 0x80 +#define SFX_NO_VELOCITY_MAP -1 /* use in velocity_map_index to indicate that no map should be used */ + +/* Instrument map types */ +#define SFX_MAP_UNKNOWN 0 +#define SFX_MAP_MT32 1 /* Original MT-32 map format */ +#define SFX_MAP_MT32_GM 2 /* More recent map format used for both MT-32 and GM */ + +/* Patch not mapped */ +#define SFX_UNMAPPED -1 +/* Patch mapped to rhythm key */ +#define SFX_MAPPED_TO_RHYTHM -2 + +/* Maximum velocity (used for scaling) */ +#define SFX_MAX_VELOCITY 128 + +typedef struct { + int patch; /* Native instrument, SFX_UNMAPPED or SFX_MAPPED_TO_RHYTHM */ + int rhythm; /* Rhythm key when patch == SFX_MAPPED_TO_RHYTHM */ +} sfx_patch_map_t; + +typedef struct { + sfx_patch_map_t patch_map[SFX_INSTRUMENTS_NR]; /* Map patch nr to which native instrument or rhythm key */ + int patch_key_shift[SFX_INSTRUMENTS_NR]; /* Shift patch key by how much? */ + int patch_volume_adjust[SFX_INSTRUMENTS_NR]; /* Adjust controller 7 by how much? */ + int patch_bend_range[SFX_INSTRUMENTS_NR]; /* Bend range in semitones or SFX_UNMAPPED for default */ + + int percussion_map[SFX_RHYTHM_NR]; /* Map percussion instrument (RHYTH_CHANNEL) to what native 'key'? */ + int percussion_volume_adjust; /* unused in SCI patches */ + + int velocity_map_index[SFX_INSTRUMENTS_NR]; /* Velocity translation map to use for that instrument */ + int velocity_maps_nr; /* How many velocity translation maps do we have? */ + byte **velocity_map; /* velocity_maps_nr entries, each of size SFX_VELOCITIES_NR */ + int percussion_velocity_map_index; /* Special index for the percussion map */ + int percussion_velocity_scale[SFX_INSTRUMENTS_NR]; /* Velocity scale (0 - SFX_PERC_MAX_VOL) */ + + size_t initialisation_block_size; + byte *initialisation_block; /* Initial MIDI commands to set up the device */ +} sfx_instrument_map_t; + +sfx_instrument_map_t * +sfx_instrument_map_new(int velocity_maps_nr); +/* Constructs a new default-initialised velocity map +** Parameters: (int) velocity_maps_nr: Number of velocity maps to allocate +** Returns : (sfx_instrument_map *) an initialised instrument map +*/ + +void +sfx_instrument_map_free(sfx_instrument_map_t *map); +/* Deallocates an instrument map +** Parameters: (sfx_instrument_map *) map: The map to deallocate, or NULL for a no-op +*/ + +sfx_instrument_map_t * +sfx_instrument_map_load_sci(byte *data, size_t length); +/* Allocate and initialise an instrument map from SCI data +** Parameters: (byte *) Pointer to the data to initialise from +** (size_t) Number of bytes to expect within +** Returns : (sfx_instrument_map_t *) An initialised instrument map for these settings, or NULL +** if `data' is NULL or `data' and `length' do not permit a valid instrument map +** If `data' is null, the function will return NULL quietly. +*/ + +sfx_instrument_map_t * +sfx_instrument_map_mt32_to_gm(byte *data, size_t size); +/* Allocate and initialise an instrument map from MT-32 patch data +** Parameters: (byte *) Pointer to the MT-32 patch data to initialise from +** (size_t) Number of bytes to expect within +** Returns : (sfx_instrument_map_t *) An initialised instrument map for these settings +** If `data' is null or invalid, the function will return a default MT-32 to GM map. +*/ + +int +sfx_instrument_map_detect(byte *data, size_t size); +/* Detects the type of patch data +** Parameters: (byte *) Pointer to the patch data +** (size_t) Number of bytes to expect within +** Returns : (int) SFX_MAP_SCI1 for an SCI1 instrument map, SFX_MAP_SCI0_MT32 for SCI0 MT-32 patch data, +** or SFX_MAP_UNKNOWN for unknown. +*/ + +midi_writer_t * +sfx_mapped_writer(midi_writer_t *writer, sfx_instrument_map_t *map); +/* Wrap a midi_writer_t into an instrument map +** Parameters: (midi_writer_t *) writer: The writer to wrap +** (sfx_instrument_map_t *) map: The map to apply to all commands going into the writer, or NULL +** Returns : (midi_writer_t *) A MIDI writer that preprocesses all data by `map' and otherwise relies on `writer' +** Effects : If successful and neccessary, this operation will send initialisation messages to the writer, as needed. +** If `map' is NULL, this returns `writer'. Otherwise it sets up a Decorator that handles translation and automatically +** deallocates the instrument map when the writer is closed. +*/ + + +#endif /* !defined(SCI_INSTRUMENT_MAP_) */ diff --git a/engines/sci/sfx/seq/map-mt32-to-gm.c b/engines/sci/sfx/seq/map-mt32-to-gm.c new file mode 100644 index 0000000000..bcaf3556f1 --- /dev/null +++ b/engines/sci/sfx/seq/map-mt32-to-gm.c @@ -0,0 +1,813 @@ +/*************************************************************************** + map-mt32-to-gm.c (C) 1999,2001 Christoph Reichenbach, TU Darmstadt + (C) 1999-2000 Rickard Lind + (C) 2008 Walter van Niftrik + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + + Roland MT-32 to General MIDI conversion: + + Rickard Lind [rpl@dd.chalmers.se] + +***************************************************************************/ + +#include +#include +#include +#include "instrument-map.h" + +#define DEBUG_MT32_TO_GM + +static char +*GM_Instrument_Names[] = { + /*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 char +*GM_Percussion_Names[] = { + /*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, +/* The preceeding percussions are not covered by the GM standard */ + /*34*/ "Acoustic Bass Drum", + /*35*/ "Bass Drum 1", + /*36*/ "Side Stick", + /*37*/ "Acoustic Snare", + /*38*/ "Hand Clap", + /*39*/ "Electric Snare", + /*40*/ "Low Floor Tom", + /*41*/ "Closed Hi-Hat", + /*42*/ "High Floor Tom", + /*43*/ "Pedal Hi-Hat", + /*44*/ "Low Tom", + /*45*/ "Open Hi-Hat", + /*46*/ "Low-Mid Tom", + /*47*/ "Hi-Mid Tom", + /*48*/ "Crash Cymbal 1", + /*49*/ "High Tom", + /*50*/ "Ride Cymbal 1", + /*51*/ "Chinese Cymbal", + /*52*/ "Ride Bell", + /*53*/ "Tambourine", + /*54*/ "Splash Cymbal", + /*55*/ "Cowbell", + /*56*/ "Crash Cymbal 2", + /*57*/ "Vibraslap", + /*58*/ "Ride Cymbal 2", + /*59*/ "Hi Bongo", + /*60*/ "Low Bongo", + /*61*/ "Mute Hi Conga", + /*62*/ "Open Hi Conga", + /*63*/ "Low Conga", + /*64*/ "High Timbale", + /*65*/ "Low Timbale", + /*66*/ "High Agogo", + /*67*/ "Low Agogo", + /*68*/ "Cabasa", + /*69*/ "Maracas", + /*70*/ "Short Whistle", + /*71*/ "Long Whistle", + /*72*/ "Short Guiro", + /*73*/ "Long Guiro", + /*74*/ "Claves", + /*75*/ "Hi Wood Block", + /*76*/ "Low Wood Block", + /*77*/ "Mute Cuica", + /*78*/ "Open Cuica", + /*79*/ "Mute Triangle", + /*80*/ "Open Triangle" +}; + +/******************************************* + * Fancy instrument mappings begin here... * + *******************************************/ + + +static struct { + char *name; + gint8 gm_instr; + gint8 gm_rhythm_key; +} MT32_PresetTimbreMaps[] = { + /*000*/ {"AcouPiano1", 0, SFX_UNMAPPED}, + /*001*/ {"AcouPiano2", 1, SFX_UNMAPPED}, + /*002*/ {"AcouPiano3", 0, SFX_UNMAPPED}, + /*003*/ {"ElecPiano1", 4, SFX_UNMAPPED}, + /*004*/ {"ElecPiano2", 5, SFX_UNMAPPED}, + /*005*/ {"ElecPiano3", 4, SFX_UNMAPPED}, + /*006*/ {"ElecPiano4", 5, SFX_UNMAPPED}, + /*007*/ {"Honkytonk ", 3, SFX_UNMAPPED}, + /*008*/ {"Elec Org 1", 16, SFX_UNMAPPED}, + /*009*/ {"Elec Org 2", 17, SFX_UNMAPPED}, + /*010*/ {"Elec Org 3", 18, SFX_UNMAPPED}, + /*011*/ {"Elec Org 4", 18, SFX_UNMAPPED}, + /*012*/ {"Pipe Org 1", 19, SFX_UNMAPPED}, + /*013*/ {"Pipe Org 2", 19, SFX_UNMAPPED}, + /*014*/ {"Pipe Org 3", 20, SFX_UNMAPPED}, + /*015*/ {"Accordion ", 21, SFX_UNMAPPED}, + /*016*/ {"Harpsi 1 ", 6, SFX_UNMAPPED}, + /*017*/ {"Harpsi 2 ", 6, SFX_UNMAPPED}, + /*018*/ {"Harpsi 3 ", 6, SFX_UNMAPPED}, + /*019*/ {"Clavi 1 ", 7, SFX_UNMAPPED}, + /*020*/ {"Clavi 2 ", 7, SFX_UNMAPPED}, + /*021*/ {"Clavi 3 ", 7, SFX_UNMAPPED}, + /*022*/ {"Celesta 1 ", 8, SFX_UNMAPPED}, + /*023*/ {"Celesta 2 ", 8, SFX_UNMAPPED}, + /*024*/ {"Syn Brass1", 62, SFX_UNMAPPED}, + /*025*/ {"Syn Brass2", 63, SFX_UNMAPPED}, + /*026*/ {"Syn Brass3", 62, SFX_UNMAPPED}, + /*027*/ {"Syn Brass4", 63, SFX_UNMAPPED}, + /*028*/ {"Syn Bass 1", 38, SFX_UNMAPPED}, + /*029*/ {"Syn Bass 2", 39, SFX_UNMAPPED}, + /*030*/ {"Syn Bass 3", 38, SFX_UNMAPPED}, + /*031*/ {"Syn Bass 4", 39, SFX_UNMAPPED}, + /*032*/ {"Fantasy ", 88, SFX_UNMAPPED}, + /*033*/ {"Harmo Pan ", 89, SFX_UNMAPPED}, + /*034*/ {"Chorale ", 52, SFX_UNMAPPED}, + /*035*/ {"Glasses ", 98, SFX_UNMAPPED}, + /*036*/ {"Soundtrack", 97, SFX_UNMAPPED}, + /*037*/ {"Atmosphere", 99, SFX_UNMAPPED}, + /*038*/ {"Warm Bell ", 89, SFX_UNMAPPED}, + /*039*/ {"Funny Vox ", 85, SFX_UNMAPPED}, + /*040*/ {"Echo Bell ", 39, SFX_UNMAPPED}, + /*041*/ {"Ice Rain ", 101, SFX_UNMAPPED}, + /*042*/ {"Oboe 2001 ", 68, SFX_UNMAPPED}, + /*043*/ {"Echo Pan ", 87, SFX_UNMAPPED}, + /*044*/ {"DoctorSolo", 86, SFX_UNMAPPED}, + /*045*/ {"Schooldaze", 103, SFX_UNMAPPED}, + /*046*/ {"BellSinger", 88, SFX_UNMAPPED}, + /*047*/ {"SquareWave", 80, SFX_UNMAPPED}, + /*048*/ {"Str Sect 1", 48, SFX_UNMAPPED}, + /*049*/ {"Str Sect 2", 48, SFX_UNMAPPED}, + /*050*/ {"Str Sect 3", 49, SFX_UNMAPPED}, + /*051*/ {"Pizzicato ", 45, SFX_UNMAPPED}, + /*052*/ {"Violin 1 ", 40, SFX_UNMAPPED}, + /*053*/ {"Violin 2 ", 40, SFX_UNMAPPED}, + /*054*/ {"Cello 1 ", 42, SFX_UNMAPPED}, + /*055*/ {"Cello 2 ", 42, SFX_UNMAPPED}, + /*056*/ {"Contrabass", 43, SFX_UNMAPPED}, + /*057*/ {"Harp 1 ", 46, SFX_UNMAPPED}, + /*058*/ {"Harp 2 ", 46, SFX_UNMAPPED}, + /*059*/ {"Guitar 1 ", 24, SFX_UNMAPPED}, + /*060*/ {"Guitar 2 ", 25, SFX_UNMAPPED}, + /*061*/ {"Elec Gtr 1", 26, SFX_UNMAPPED}, + /*062*/ {"Elec Gtr 2", 27, SFX_UNMAPPED}, + /*063*/ {"Sitar ", 104, SFX_UNMAPPED}, + /*064*/ {"Acou Bass1", 32, SFX_UNMAPPED}, + /*065*/ {"Acou Bass2", 33, SFX_UNMAPPED}, + /*066*/ {"Elec Bass1", 34, SFX_UNMAPPED}, + /*067*/ {"Elec Bass2", 39, SFX_UNMAPPED}, + /*068*/ {"Slap Bass1", 36, SFX_UNMAPPED}, + /*069*/ {"Slap Bass2", 37, SFX_UNMAPPED}, + /*070*/ {"Fretless 1", 35, SFX_UNMAPPED}, + /*071*/ {"Fretless 2", 35, SFX_UNMAPPED}, + /*072*/ {"Flute 1 ", 73, SFX_UNMAPPED}, + /*073*/ {"Flute 2 ", 73, SFX_UNMAPPED}, + /*074*/ {"Piccolo 1 ", 72, SFX_UNMAPPED}, + /*075*/ {"Piccolo 2 ", 72, SFX_UNMAPPED}, + /*076*/ {"Recorder ", 74, SFX_UNMAPPED}, + /*077*/ {"Panpipes ", 75, SFX_UNMAPPED}, + /*078*/ {"Sax 1 ", 64, SFX_UNMAPPED}, + /*079*/ {"Sax 2 ", 65, SFX_UNMAPPED}, + /*080*/ {"Sax 3 ", 66, SFX_UNMAPPED}, + /*081*/ {"Sax 4 ", 67, SFX_UNMAPPED}, + /*082*/ {"Clarinet 1", 71, SFX_UNMAPPED}, + /*083*/ {"Clarinet 2", 71, SFX_UNMAPPED}, + /*084*/ {"Oboe ", 68, SFX_UNMAPPED}, + /*085*/ {"Engl Horn ", 69, SFX_UNMAPPED}, + /*086*/ {"Bassoon ", 70, SFX_UNMAPPED}, + /*087*/ {"Harmonica ", 22, SFX_UNMAPPED}, + /*088*/ {"Trumpet 1 ", 56, SFX_UNMAPPED}, + /*089*/ {"Trumpet 2 ", 56, SFX_UNMAPPED}, + /*090*/ {"Trombone 1", 57, SFX_UNMAPPED}, + /*091*/ {"Trombone 2", 57, SFX_UNMAPPED}, + /*092*/ {"Fr Horn 1 ", 60, SFX_UNMAPPED}, + /*093*/ {"Fr Horn 2 ", 60, SFX_UNMAPPED}, + /*094*/ {"Tuba ", 58, SFX_UNMAPPED}, + /*095*/ {"Brs Sect 1", 61, SFX_UNMAPPED}, + /*096*/ {"Brs Sect 2", 61, SFX_UNMAPPED}, + /*097*/ {"Vibe 1 ", 11, SFX_UNMAPPED}, + /*098*/ {"Vibe 2 ", 11, SFX_UNMAPPED}, + /*099*/ {"Syn Mallet", 15, SFX_UNMAPPED}, + /*100*/ {"Wind Bell ", 88, SFX_UNMAPPED}, + /*101*/ {"Glock ", 9, SFX_UNMAPPED}, + /*102*/ {"Tube Bell ", 14, SFX_UNMAPPED}, + /*103*/ {"Xylophone ", 13, SFX_UNMAPPED}, + /*104*/ {"Marimba ", 12, SFX_UNMAPPED}, + /*105*/ {"Koto ", 107, SFX_UNMAPPED}, + /*106*/ {"Sho ", 111, SFX_UNMAPPED}, + /*107*/ {"Shakuhachi", 77, SFX_UNMAPPED}, + /*108*/ {"Whistle 1 ", 78, SFX_UNMAPPED}, + /*109*/ {"Whistle 2 ", 78, SFX_UNMAPPED}, + /*110*/ {"BottleBlow", 76, SFX_UNMAPPED}, + /*111*/ {"BreathPipe", 121, SFX_UNMAPPED}, + /*112*/ {"Timpani ", 47, SFX_UNMAPPED}, + /*113*/ {"MelodicTom", 117, SFX_UNMAPPED}, + /*114*/ {"Deep Snare", SFX_MAPPED_TO_RHYTHM, 37}, + /*115*/ {"Elec Perc1", 115, SFX_UNMAPPED}, /* ? */ + /*116*/ {"Elec Perc2", 118, SFX_UNMAPPED}, /* ? */ + /*117*/ {"Taiko ", 116, SFX_UNMAPPED}, + /*118*/ {"Taiko Rim ", 118, SFX_UNMAPPED}, + /*119*/ {"Cymbal ", SFX_MAPPED_TO_RHYTHM, 50}, + /*120*/ {"Castanets ", SFX_MAPPED_TO_RHYTHM, SFX_UNMAPPED}, + /*121*/ {"Triangle ", 112, SFX_UNMAPPED}, + /*122*/ {"Orche Hit ", 55, SFX_UNMAPPED}, + /*123*/ {"Telephone ", 124, SFX_UNMAPPED}, + /*124*/ {"Bird Tweet", 123, SFX_UNMAPPED}, + /*125*/ {"OneNoteJam", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? */ + /*126*/ {"WaterBells", 98, SFX_UNMAPPED}, + /*127*/ {"JungleTune", SFX_UNMAPPED, SFX_UNMAPPED} /* ? */ +}; + +static struct { + char *name; + gint8 gm_instr; + gint8 gm_rhythmkey; +} MT32_RhythmTimbreMaps[] = { + /*00*/ {"Acou BD ", SFX_MAPPED_TO_RHYTHM, 34}, + /*01*/ {"Acou SD ", SFX_MAPPED_TO_RHYTHM, 37}, + /*02*/ {"Acou HiTom", 117, 49}, + /*03*/ {"AcouMidTom", 117, 46}, + /*04*/ {"AcouLowTom", 117, 40}, + /*05*/ {"Elec SD ", SFX_MAPPED_TO_RHYTHM, 39}, + /*06*/ {"Clsd HiHat", SFX_MAPPED_TO_RHYTHM, 41}, + /*07*/ {"OpenHiHat1", SFX_MAPPED_TO_RHYTHM, 45}, + /*08*/ {"Crash Cym ", SFX_MAPPED_TO_RHYTHM, 48}, + /*09*/ {"Ride Cym ", SFX_MAPPED_TO_RHYTHM, 50}, + /*10*/ {"Rim Shot ", SFX_MAPPED_TO_RHYTHM, 36}, + /*11*/ {"Hand Clap ", SFX_MAPPED_TO_RHYTHM, 38}, + /*12*/ {"Cowbell ", SFX_MAPPED_TO_RHYTHM, 55}, + /*13*/ {"Mt HiConga", SFX_MAPPED_TO_RHYTHM, 61}, + /*14*/ {"High Conga", SFX_MAPPED_TO_RHYTHM, 62}, + /*15*/ {"Low Conga ", SFX_MAPPED_TO_RHYTHM, 63}, + /*16*/ {"Hi Timbale", SFX_MAPPED_TO_RHYTHM, 64}, + /*17*/ {"LowTimbale", SFX_MAPPED_TO_RHYTHM, 65}, + /*18*/ {"High Bongo", SFX_MAPPED_TO_RHYTHM, 59}, + /*19*/ {"Low Bongo ", SFX_MAPPED_TO_RHYTHM, 60}, + /*20*/ {"High Agogo", 113, 66}, + /*21*/ {"Low Agogo ", 113, 67}, + /*22*/ {"Tambourine", SFX_MAPPED_TO_RHYTHM, 53}, + /*23*/ {"Claves ", SFX_MAPPED_TO_RHYTHM, 74}, + /*24*/ {"Maracas ", SFX_MAPPED_TO_RHYTHM, 69}, + /*25*/ {"SmbaWhis L", 78, 71}, + /*26*/ {"SmbaWhis S", 78, 70}, + /*27*/ {"Cabasa ", SFX_MAPPED_TO_RHYTHM, 68}, + /*28*/ {"Quijada ", SFX_MAPPED_TO_RHYTHM, 72}, + /*29*/ {"OpenHiHat2", SFX_MAPPED_TO_RHYTHM, 43} +}; + +static gint8 +MT32_PresetRhythmKeymap[] = { + SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, + SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, + SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, + SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, 34, 34, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, SFX_UNMAPPED, SFX_UNMAPPED, 53, SFX_UNMAPPED, 55, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, SFX_UNMAPPED, 74, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, + SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, + SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, + SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, + SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, + SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED +}; + +/* +++ - Don't change unless you've got a good reason + ++ - Looks good, sounds ok + + - Not too bad, but is it right? + ? - Where do I map this one? + ?? - Any good ideas? + ??? - I'm clueless? + R - Rhythm... */ +static struct { + char *name; + gint8 gm_instr; + gint8 gm_rhythm_key; +} MT32_MemoryTimbreMaps[] = { + {"AccPnoKA2 ", 1, SFX_UNMAPPED}, /* ++ (KQ1) */ + {"Acou BD ", SFX_MAPPED_TO_RHYTHM, 34}, /* R (PQ2) */ + {"Acou SD ", SFX_MAPPED_TO_RHYTHM, 37}, /* R (PQ2) */ + {"AcouPnoKA ", 0, SFX_UNMAPPED}, /* ++ (KQ1) */ + {"BASS ", 32, SFX_UNMAPPED}, /* + (LSL3) */ + {"BASSOONPCM", 70, SFX_UNMAPPED}, /* + (CB) */ + {"BEACH WAVE", 122, SFX_UNMAPPED}, /* + (LSL3) */ + {"BagPipes ", 109, SFX_UNMAPPED}, + {"BassPizzMS", 45, SFX_UNMAPPED}, /* ++ (HQ) */ + {"BassoonKA ", 70, SFX_UNMAPPED}, /* ++ (KQ1) */ + {"Bell MS", 112, SFX_UNMAPPED}, /* ++ (iceMan) */ + {"Bells MS", 112, SFX_UNMAPPED}, /* + (HQ) */ + {"Big Bell ", 14, SFX_UNMAPPED}, /* + (CB) */ + {"Bird Tweet", 123, SFX_UNMAPPED}, + {"BrsSect MS", 61, SFX_UNMAPPED}, /* +++ (iceMan) */ + {"CLAPPING ", 126, SFX_UNMAPPED}, /* ++ (LSL3) */ + {"Cabasa ", SFX_MAPPED_TO_RHYTHM, 68}, /* R (HBoG) */ + {"Calliope ", 82, SFX_UNMAPPED}, /* +++ (HQ) */ + {"CelticHarp", 46, SFX_UNMAPPED}, /* ++ (CoC) */ + {"Chicago MS", 1, SFX_UNMAPPED}, /* ++ (iceMan) */ + {"Chop ", 117, SFX_UNMAPPED}, + {"Chorale MS", 52, SFX_UNMAPPED}, /* + (CoC) */ + {"ClarinetMS", 71, SFX_UNMAPPED}, + {"Claves ", SFX_MAPPED_TO_RHYTHM, 74}, /* R (PQ2) */ + {"Claw MS", 118, SFX_UNMAPPED}, /* + (HQ) */ + {"ClockBell ", 14, SFX_UNMAPPED}, /* + (CB) */ + {"ConcertCym", SFX_MAPPED_TO_RHYTHM, 54}, /* R ? (KQ1) */ + {"Conga MS", SFX_MAPPED_TO_RHYTHM, 63}, /* R (HQ) */ + {"CoolPhone ", 124, SFX_UNMAPPED}, /* ++ (LSL3) */ + {"CracklesMS", 115, SFX_UNMAPPED}, /* ? (CoC, HQ) */ + {"CreakyD MS", SFX_UNMAPPED, SFX_UNMAPPED}, /* ??? (KQ1) */ + {"Cricket ", 120, SFX_UNMAPPED}, /* ? (CB) */ + {"CrshCymbMS", SFX_MAPPED_TO_RHYTHM, 56}, /* R +++ (iceMan) */ + {"CstlGateMS", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (HQ) */ + {"CymSwellMS", SFX_MAPPED_TO_RHYTHM, 54}, /* R ? (CoC, HQ) */ + {"CymbRollKA", SFX_MAPPED_TO_RHYTHM, 56}, /* R ? (KQ1) */ + {"Cymbal Lo ", SFX_UNMAPPED, SFX_UNMAPPED}, /* R ? (LSL3) */ + {"card ", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (HBoG) */ + {"DirtGtr MS", 30, SFX_UNMAPPED}, /* + (iceMan) */ + {"DirtGtr2MS", 29, SFX_UNMAPPED}, /* + (iceMan) */ + {"E Bass MS", 33, SFX_UNMAPPED}, /* + (SQ3) */ + {"ElecBassMS", 33, SFX_UNMAPPED}, + {"ElecGtr MS", 27, SFX_UNMAPPED}, /* ++ (iceMan) */ + {"EnglHornMS", 69, SFX_UNMAPPED}, + {"FantasiaKA", 88, SFX_UNMAPPED}, + {"Fantasy ", 99, SFX_UNMAPPED}, /* + (PQ2) */ + {"Fantasy2MS", 99, SFX_UNMAPPED}, /* ++ (CoC, HQ) */ + {"Filter MS", 95, SFX_UNMAPPED}, /* +++ (iceMan) */ + {"Filter2 MS", 95, SFX_UNMAPPED}, /* ++ (iceMan) */ + {"Flame2 MS", 121, SFX_UNMAPPED}, /* ? (HQ) */ + {"Flames MS", 121, SFX_UNMAPPED}, /* ? (HQ) */ + {"Flute MS", 73, SFX_UNMAPPED}, /* +++ (HQ) */ + {"FogHorn MS", 58, SFX_UNMAPPED}, + {"FrHorn1 MS", 60, SFX_UNMAPPED}, /* +++ (HQ) */ + {"FunnyTrmp ", 56, SFX_UNMAPPED}, /* ++ (CB) */ + {"GameSnd MS", 80, SFX_UNMAPPED}, + {"Glock MS", 9, SFX_UNMAPPED}, /* +++ (HQ) */ + {"Gunshot ", 127, SFX_UNMAPPED}, /* +++ (CB) */ + {"Hammer MS", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (HQ) */ + {"Harmonica2", 22, SFX_UNMAPPED}, /* +++ (CB) */ + {"Harpsi 1 ", 6, SFX_UNMAPPED}, /* + (HBoG) */ + {"Harpsi 2 ", 6, SFX_UNMAPPED}, /* +++ (CB) */ + {"Heart MS", 116, SFX_UNMAPPED}, /* ? (iceMan) */ + {"Horse1 MS", 115, SFX_UNMAPPED}, /* ? (CoC, HQ) */ + {"Horse2 MS", 115, SFX_UNMAPPED}, /* ? (CoC, HQ) */ + {"InHale MS", 121, SFX_UNMAPPED}, /* ++ (iceMan) */ + {"KNIFE ", 120, SFX_UNMAPPED}, /* ? (LSL3) */ + {"KenBanjo ", 105, SFX_UNMAPPED}, /* +++ (CB) */ + {"Kiss MS", 25, SFX_UNMAPPED}, /* ++ (HQ) */ + {"KongHit ", SFX_UNMAPPED, SFX_UNMAPPED}, /* ??? (KQ1) */ + {"Koto ", 107, SFX_UNMAPPED}, /* +++ (PQ2) */ + {"Laser MS", 81, SFX_UNMAPPED}, /* ?? (HQ) */ + {"Meeps MS", 62, SFX_UNMAPPED}, /* ? (HQ) */ + {"MTrak MS", 62, SFX_UNMAPPED}, /* ?? (iceMan) */ + {"MachGun MS", 127, SFX_UNMAPPED}, /* ? (iceMan) */ + {"OCEANSOUND", 122, SFX_UNMAPPED}, /* + (LSL3) */ + {"Oboe 2001 ", 68, SFX_UNMAPPED}, /* + (PQ2) */ + {"Ocean MS", 122, SFX_UNMAPPED}, /* + (iceMan) */ + {"PPG 2.3 MS", 75, SFX_UNMAPPED}, /* ? (iceMan) */ + {"PianoCrank", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (CB) */ + {"PicSnareMS", SFX_MAPPED_TO_RHYTHM, 39}, /* R ? (iceMan) */ + {"PiccoloKA ", 72, SFX_UNMAPPED}, /* +++ (KQ1) */ + {"PinkBassMS", 39, SFX_UNMAPPED}, + {"Pizz2 ", 45, SFX_UNMAPPED}, /* ++ (CB) */ + {"Portcullis", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (KQ1) */ + {"Raspbry MS", 81, SFX_UNMAPPED}, /* ? (HQ) */ + {"RatSqueek ", 72, SFX_UNMAPPED}, /* ? (CB, CoC) */ + {"Record78 ", SFX_UNMAPPED, SFX_UNMAPPED}, /* +++ (CB) */ + {"RecorderMS", 74, SFX_UNMAPPED}, /* +++ (CoC) */ + {"Red Baron ", 125, SFX_UNMAPPED}, /* ? (CB) */ + {"ReedPipMS ", 20, SFX_UNMAPPED}, /* +++ (Coc) */ + {"RevCymb MS", 119, SFX_UNMAPPED}, + {"RifleShot ", 127, SFX_UNMAPPED}, /* + (CB) */ + {"RimShot MS", SFX_MAPPED_TO_RHYTHM, 36}, /* R */ + {"SHOWER ", 52, SFX_UNMAPPED}, /* ? (LSL3) */ + {"SQ Bass MS", 32, SFX_UNMAPPED}, /* + (SQ3) */ + {"ShakuVibMS", 79, SFX_UNMAPPED}, /* + (iceMan) */ + {"SlapBassMS", 36, SFX_UNMAPPED}, /* +++ (iceMan) */ + {"Snare MS", SFX_MAPPED_TO_RHYTHM, 37}, /* R (HQ) */ + {"Some Birds", 123, SFX_UNMAPPED}, /* + (CB) */ + {"Sonar MS", 78, SFX_UNMAPPED}, /* ? (iceMan) */ + {"Soundtrk2 ", 97, SFX_UNMAPPED}, /* +++ (CB) */ + {"Soundtrack", 97, SFX_UNMAPPED}, /* ++ (CoC) */ + {"SqurWaveMS", 80, SFX_UNMAPPED}, + {"StabBassMS", 34, SFX_UNMAPPED}, /* + (iceMan) */ + {"SteelDrmMS", 114, SFX_UNMAPPED}, /* +++ (iceMan) */ + {"StrSect1MS", 48, SFX_UNMAPPED}, /* ++ (HQ) */ + {"String MS", 45, SFX_UNMAPPED}, /* + (CoC) */ + {"Syn-Choir ", 91, SFX_UNMAPPED}, + {"Syn Brass4", 63, SFX_UNMAPPED}, /* ++ (PQ2) */ + {"SynBass MS", 38, SFX_UNMAPPED}, + {"SwmpBackgr", 120, SFX_UNMAPPED}, /* ?? (CB,HQ) */ + {"T-Bone2 MS", 57, SFX_UNMAPPED}, /* +++ (HQ) */ + {"Taiko ", 116, 34}, /* +++ (Coc) */ + {"Taiko Rim ", 118, 36}, /* +++ (LSL3) */ + {"Timpani1 ", 47, SFX_UNMAPPED}, /* +++ (CB) */ + {"Tom MS", 117, 47}, /* +++ (iceMan) */ + {"Toms MS", 117, 47}, /* +++ (CoC, HQ) */ + {"Tpt1prtl ", 56, SFX_UNMAPPED}, /* +++ (KQ1) */ + {"TriangleMS", 112, 80}, /* R (CoC) */ + {"Trumpet 1 ", 56, SFX_UNMAPPED}, /* +++ (CoC) */ + {"Type MS", 114, SFX_UNMAPPED}, /* ? (iceMan) */ + {"WaterBells", 98, SFX_UNMAPPED}, /* + (PQ2) */ + {"WaterFallK", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (KQ1) */ + {"Whiporill ", 123, SFX_UNMAPPED}, /* + (CB) */ + {"Wind ", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (CB) */ + {"Wind MS", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (HQ, iceMan) */ + {"Wind2 MS", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (CoC) */ + {"Woodpecker", 115, SFX_UNMAPPED}, /* ? (CB) */ + {"WtrFall MS", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (CoC, HQ, iceMan) */ + {0, 0} +}; + +static gint8 +lookup_instrument(char *iname) +{ + int i = 0; + + while (MT32_MemoryTimbreMaps[i].name) { + if (strncasecmp(iname, MT32_MemoryTimbreMaps[i].name, 10) == 0) + return MT32_MemoryTimbreMaps[i].gm_instr; + i++; + } + return SFX_UNMAPPED; +} + +static gint8 +lookup_rhythm_key(char *iname) +{ + int i = 0; + + while (MT32_MemoryTimbreMaps[i].name) { + if (strncasecmp(iname, MT32_MemoryTimbreMaps[i].name, 10) == 0) + return MT32_MemoryTimbreMaps[i].gm_rhythm_key; + i++; + } + return SFX_UNMAPPED; +} + +static void +print_map(int sci, int ins, int rhythm, int mt32) +{ +#ifdef DEBUG_MT32_TO_GM + if (ins == SFX_UNMAPPED || (ins == SFX_MAPPED_TO_RHYTHM && rhythm == SFX_UNMAPPED)) { + sciprintf("[MT32-to-GM] No mapping available for [%i] `%s' (%i)\n", + sci, MT32_PresetTimbreMaps[mt32].name, mt32); + return; + } + + if (ins == SFX_MAPPED_TO_RHYTHM) { + sciprintf("[MT32-to-GM] Mapping [%i] `%s' (%i) to `%s' [R] (%i)\n", + sci, MT32_PresetTimbreMaps[mt32].name, mt32, + GM_Percussion_Names[rhythm], rhythm); + return; + } + + sciprintf("[MT32-to-GM] Mapping [%i] `%s' (%i) to `%s' (%i)\n", + sci, MT32_PresetTimbreMaps[mt32].name, mt32, + GM_Instrument_Names[ins], ins); +#endif +} + +static void +print_map_mem(int sci, int ins, int rhythm, char *mt32) +{ +#ifdef DEBUG_MT32_TO_GM + char name[11]; + + strncpy(name, mt32, 10); + name[10] = 0; + + if (ins == SFX_UNMAPPED || (ins == SFX_MAPPED_TO_RHYTHM && rhythm == SFX_UNMAPPED)) { + sciprintf("[MT32-to-GM] No mapping available for [%i] `%s'\n", + sci, name); + return; + } + + if (ins == SFX_MAPPED_TO_RHYTHM) { + sciprintf("[MT32-to-GM] Mapping [%i] `%s' to `%s' [R] (%i)\n", + sci, name, GM_Percussion_Names[rhythm], rhythm); + return; + } + + sciprintf("[MT32-to-GM] Mapping [%i] `%s' to `%s' (%i)\n", + sci, name, GM_Instrument_Names[ins], ins); +#endif +} + +static void +print_map_rhythm(int sci, int ins, int rhythm, int mt32) +{ +#ifdef DEBUG_MT32_TO_GM + if (ins == SFX_UNMAPPED || (ins == SFX_MAPPED_TO_RHYTHM && rhythm == SFX_UNMAPPED)) { + sciprintf("[MT32-to-GM] No mapping available for [%i] `%s' [R] (%i)\n", + sci, MT32_RhythmTimbreMaps[mt32].name, mt32); + return; + } + + if (ins == SFX_MAPPED_TO_RHYTHM) { + sciprintf("[MT32-to-GM] Mapping [%i] `%s' [R] (%i) to `%s' [R] (%i)\n", + sci, MT32_RhythmTimbreMaps[mt32].name, mt32, + GM_Percussion_Names[rhythm], rhythm); + return; + } + + sciprintf("[MT32-to-GM] Mapping [%i] `%s' [R] (%i) to `%s' (%i)\n", + sci, MT32_RhythmTimbreMaps[mt32].name, mt32, + GM_Instrument_Names[ins], ins); +#endif +} + +static void +print_map_rhythm_mem(int sci, int rhythm, char *mt32) +{ +#ifdef DEBUG_MT32_TO_GM + char name[11]; + + strncpy(name, mt32, 10); + name[10] = 0; + + if (rhythm == SFX_UNMAPPED) { + sciprintf("[MT32-to-GM] No mapping available for [%i] `%s'\n", + sci, name); + return; + } + + sciprintf("[MT32-to-GM] Mapping [%i] `%s' to `%s' (%i)\n", + sci, name, GM_Percussion_Names[rhythm], rhythm); +#endif +} + +sfx_instrument_map_t * +sfx_instrument_map_mt32_to_gm(byte *data, size_t size) +{ + int memtimbres, patches; + guint8 group, number, keyshift, finetune, bender_range; + guint8 *patchpointer; + guint32 pos; + sfx_instrument_map_t * map; + int i; + int type; + + map = sfx_instrument_map_new(0); + + for (i = 0; i < SFX_INSTRUMENTS_NR; i++) { + map->patch_map[i].patch = MT32_PresetTimbreMaps[i].gm_instr; + map->patch_key_shift[i] = 0; + map->patch_volume_adjust[i] = 0; + map->patch_bend_range[i] = 12; + map->velocity_map_index[i] = SFX_NO_VELOCITY_MAP; + } + + map->percussion_volume_adjust = 0; + map->percussion_velocity_map_index = SFX_NO_VELOCITY_MAP; + + for (i = 0; i < SFX_RHYTHM_NR; i++) { + map->percussion_map[i] = MT32_PresetRhythmKeymap[i]; + map->percussion_velocity_scale[i] = SFX_MAX_VELOCITY; + } + + if (!data) { + sciprintf("[MT32-to-GM] No MT-32 patch data supplied, using default mapping\n"); + return map; + } + + type = sfx_instrument_map_detect(data, size); + + if (type == SFX_MAP_UNKNOWN) { + sciprintf("[MT32-to-GM] Patch data format unknown, using default mapping\n"); + return map; + } + if (type == SFX_MAP_MT32_GM) { + sciprintf("[MT32-to-GM] Patch data format not supported, using default mapping\n"); + return map; + } + + memtimbres = *(data + 0x1EB); + pos = 0x1EC + memtimbres * 0xF6; + + if (size > pos && ((0x100 * *(data + pos) + *(data +pos + 1)) == 0xABCD)) { + patches = 96; + pos += 2 + 8 * 48; + } else + patches = 48; + + sciprintf("[MT32-to-GM] %d MT-32 Patches detected\n", patches); + sciprintf("[MT32-to-GM] %d MT-32 Memory Timbres\n", memtimbres); + + sciprintf("[MT32-to-GM] Mapping patches..\n"); + + for (i = 0; i < patches; i++) { + char *name; + + if (i < 48) + patchpointer = data + 0x6B + 8 * i; + else + patchpointer = data + 0x1EC + 8 * (i - 48) + memtimbres * 0xF6 + 2; + + group = *patchpointer; + number = *(patchpointer + 1); + keyshift = *(patchpointer + 2); + finetune = *(patchpointer + 3); + bender_range = *(patchpointer + 4); + + switch (group) { + case 0: + map->patch_map[i].patch = MT32_PresetTimbreMaps[number].gm_instr; + map->patch_map[i].rhythm = MT32_PresetTimbreMaps[number].gm_rhythm_key; + print_map(i, map->patch_map[i].patch, map->patch_map[i].rhythm, number); + break; + case 1: + map->patch_map[i].patch = MT32_PresetTimbreMaps[number + 64].gm_instr; + map->patch_map[i].rhythm = MT32_PresetTimbreMaps[number + 64].gm_rhythm_key; + print_map(i, map->patch_map[i].patch, map->patch_map[i].rhythm, number + 64); + break; + case 2: + name = (char *) data + 0x1EC + number * 0xF6; + map->patch_map[i].patch = lookup_instrument(name); + map->patch_map[i].rhythm = SFX_UNMAPPED; + print_map_mem(i, map->patch_map[i].patch, map->patch_map[i].rhythm, name); + break; + case 3: + map->patch_map[i].patch = MT32_RhythmTimbreMaps[number].gm_instr; + map->patch_map[i].rhythm = SFX_UNMAPPED; + print_map_rhythm(i, map->patch_map[i].patch, map->patch_map[i].rhythm, number); + break; + default: + break; + } + + /* map->patch_key_shift[i] = (int) (keyshift & 0x3F) - 24; */ + map->patch_bend_range[i] = bender_range & 0x1F; + } + + if (size > pos && ((0x100 * *(data + pos) + *(data + pos + 1)) == 0xDCBA)) { + sciprintf("[MT32-to-GM] Mapping percussion..\n"); + + for (i = 0; i < 64 ; i++) { + number = *(data + pos + 4 * i + 2); + + if (number < 64) { + char *name = (char *) data + 0x1EC + number * 0xF6; + map->percussion_map[i + 23] = lookup_rhythm_key(name); + print_map_rhythm_mem(i, map->percussion_map[i + 23], name); + } else { + if (number < 94) { + map->percussion_map[i + 23] = MT32_RhythmTimbreMaps[number - 64].gm_rhythmkey; + print_map_rhythm(i, SFX_MAPPED_TO_RHYTHM, map->percussion_map[i + 23], number - 64); + } else + map->percussion_map[i + 23] = SFX_UNMAPPED; + } + + map->percussion_velocity_scale[i + 23] = *(data + pos + 4 * i + 3) * SFX_MAX_VELOCITY / 100; + } + } + + return map; +} + diff --git a/engines/sci/sfx/seq/mt32.c b/engines/sci/sfx/seq/mt32.c new file mode 100644 index 0000000000..b71d474927 --- /dev/null +++ b/engines/sci/sfx/seq/mt32.c @@ -0,0 +1,480 @@ +/*************************************************************************** + midi_mt32.c Copyright (C) 2000,2001 Rickard Lind, Solomon Peachy + mt32.c Copyright (C) 2002..04 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#include +#ifdef HAVE_UNISTD_H +# include +#endif +#include "../sequencer.h" +#include "instrument-map.h" +#include + +#ifdef _WIN32 +# include +# include +#endif + +#ifdef __BEOS__ +# include +#endif + +static int delta = 0; /* Accumulated delta time */ +static midi_writer_t *midi_writer = NULL; + +static int midi_mt32_poke(guint32 address, guint8 *data, unsigned int n); +static int midi_mt32_poke_gather(guint32 address, guint8 *data1, unsigned int count1, + guint8 *data2, unsigned int count2); +static int midi_mt32_write_block(guint8 *data, unsigned int count); +static int midi_mt32_sysex_delay(void); +static int midi_mt32_volume(guint8 volume); +static int midi_mt32_reverb(int param); +static int midi_mt32_event(byte command, int argc, byte *argv); +static int midi_mt32_allstop(void); + +static int type; +static guint8 sysex_buffer[266] = {0xF0, 0x41, 0x10, 0x16, 0x12}; +static guint8 default_reverb; +static char shutdown_msg[20]; + +static long mt32_init_sec, mt32_init_usec; /* Time at initialisation */ +static int mt32_init_delay = 0; /* Used to count the number of ticks (1/60s of a second) we should + ** wait before initialisation has been completed */ + +/* timbre, volume, panpot, reverb. keys 24-87 (64 keys)*/ +static guint8 default_rhythm_keymap[256] = { /* MT-32 default */ + 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, /* 24-27 */ + 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, + 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x40,0x64,7,1, + 0x40,0x64,7,1, 0x4a,0x64,6,1, 0x41,0x64,7,1, 0x4b,0x64,8,1, + 0x45,0x64,6,1, 0x44,0x64,11,1, 0x46,0x64,6,1, 0x44,0x64,11,1, + 0x5d,0x64,6,1, 0x43,0x64,8,1, 0x47,0x64,6,1, 0x43,0x64,8,1, + 0x42,0x64,3,1, 0x48,0x64,6,1, 0x42,0x64,3,1, 0x49,0x64,8,1, + 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x56,0x64,9,1, 0x7f,0x64,7,1, + 0x4c,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, + 0x52,0x64,2,1, 0x53,0x64,4,1, 0x4d,0x64,8,1, 0x4e,0x64,9,1, + 0x4f,0x64,10,1, 0x50,0x64,7,1, 0x51,0x64,5,1, 0x54,0x64,2,1, + 0x55,0x64,2,1, 0x5b,0x64,9,1, 0x58,0x64,4,1, 0x5a,0x64,9,1, + 0x59,0x64,9,1, 0x5c,0x64,10,1, 0x7f,0x64,7,1, 0x57,0x64,12,1, + 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, + 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, + 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1 /* 84-87 */ +}; + +static guint8 default_partial_reserve[9] = { /* MT-32 DEFAULT */ + 3, 10, 6, 4, 3, 0, 0, 0, 6 }; + +static struct { + guint8 mode; + guint8 time; + guint8 level; +} mt32_reverb[11]; + + +static int +midiout_write_block(byte *buf, int len, int delta) +{ + if (delta) + midi_writer->delay(midi_writer, delta); + + return midi_writer->write(midi_writer, buf, len); +} + +/* The following is the result of some experimenting, trying to approach the MT32's processing speed */ +#define MAGIC_MIDIOUT_DELAY 40 + +static int +midiout_write_delayed_block(byte *buf, int len) + /* Only used for initial programming */ +{ + int rv = midiout_write_block(buf, len, 0); + int delay = 1 + (len / MAGIC_MIDIOUT_DELAY); + + midi_writer->delay(midi_writer, delay); + + mt32_init_delay += delay; /* Keep track of delay times */ + + return rv; +} + +/* send default rhythm map and reserve */ +int midi_mt32_defaults(guint8 volume, guint8 reverb) { + printf("MT-32: Writing Default Rhythm key map\n"); + midi_mt32_poke(0x030110, default_rhythm_keymap, 256); + + printf("MT-32: Writing Default Partial Reserve\n"); + midi_mt32_poke(0x100004, default_partial_reserve, 9); + + if (reverb) { + mt32_reverb[0].mode = 0; + mt32_reverb[0].time = 5; + mt32_reverb[0].level = 3; + default_reverb = 0; + + printf("MT-32: Setting up default reverb levels\n"); + midi_mt32_reverb(default_reverb); + } + + if (volume) { + printf("MT-32: Setting default volume (%d)\n", volume); + midi_mt32_volume(volume); + } + + return SFX_OK; +} + +int midi_mt32_open(int length, byte *data, int length2, byte *data2, void *dev) +{ + guint8 unknown_sysex[6] = {0x16, 0x16, 0x16, 0x16, 0x16, 0x16}; + guint8 i, memtimbres; + unsigned int block2, block3; + + if (!dev) { + fprintf(stderr, "Attempt to use MT-32 sequencer without device\n"); + return SFX_ERROR; + } + + sci_gettime(&mt32_init_sec, &mt32_init_usec); + + midi_writer = (midi_writer_t *) dev; + + midi_mt32_allstop(); + + if (!data) { + type = SFX_MAP_UNKNOWN; + sciprintf("MT-32: No patch.001 found, using defaults\n"); + } else { + type = sfx_instrument_map_detect(data, length); + if (type == SFX_MAP_UNKNOWN) + sciprintf("MT-32: Unknown patch.001 format, using defaults\n"); + else + sciprintf("MT-32: Programming Roland MT-32 with patch.001 (v%i) %d bytes\n", type, length); + } + + if (type == SFX_MAP_MT32) { + /* Display MT-32 initialization message */ + printf("MT-32: Displaying Text: \"%.20s\"\n", data + 20); + midi_mt32_poke(0x200000, data + 20, 20); + + /* Cache a copy of the shutdown message */ + memcpy(shutdown_msg, data + 40, 20); + + /* Write Patches (48 or 96) */ + memtimbres = data[491]; + block2 = (memtimbres * 246) + 492; + printf("MT-32: Writing Patches #01 - #32\n"); + midi_mt32_poke(0x050000, data + 107, 256); + if ((length > block2) && + data[block2] == 0xAB && + data[block2 + 1] == 0xCD) { + printf("MT-32: Writing Patches #33 - #64\n"); + midi_mt32_poke_gather(0x050200, data + 363, 128, data + block2 + 2, 128); + printf("MT-32: Writing Patches #65 - #96\n"); + midi_mt32_poke(0x050400, data + block2 + 130, 256); + block3 = block2 + 386; + } else { + printf("MT-32: Writing Patches #33 - #48\n"); + midi_mt32_poke(0x050200, data + 363, 128); + block3 = block2; + } + /* Write Memory Timbres */ + for (i = 0; i < memtimbres; i++) { + printf("MT-32: Writing Memory Timbre #%02d: \"%.10s\"\n", + i + 1, data + 492 + i * 246); + midi_mt32_poke(0x080000 + (i << 9), data + 492 + i * 246, 246); + } + /* Write Rhythm key map and Partial Reserve */ + if ((length > block3) && + data[block3] == 0xDC && + data[block3 + 1] == 0xBA) { + printf("MT-32: Writing Rhythm key map\n"); + midi_mt32_poke(0x030110, data + block3 + 2, 256); + printf("MT-32: Writing Partial Reserve\n"); + midi_mt32_poke(0x100004, data + block3 + 258, 9); + } else { + midi_mt32_defaults(0,0); /* send default keymap/reserve */ + } + /* Display MT-32 initialization done message */ + printf("MT-32: Displaying Text: \"%.20s\"\n", data); + midi_mt32_poke(0x200000, data, 20); + /* Write undocumented MT-32(?) SysEx */ + printf("MT-32: Writing {F0 41 10 16 12 52 00 0A 16 16 16 16 16 16 20 F7}\n"); + midi_mt32_poke(0x52000A, unknown_sysex, 6); + printf("MT-32: Setting up reverb levels\n"); + default_reverb = data[0x3e]; + memcpy(mt32_reverb,data+ 0x4a, 3 * 11); + midi_mt32_reverb(default_reverb); + printf("MT-32: Setting default volume (%d)\n", data[0x3c]); + midi_mt32_volume(data[0x3c]); + return 0; + } else if (type == SFX_MAP_MT32_GM) { + printf("MT-32: Loading SysEx bank\n"); + midi_mt32_write_block(data + 1155, (data[1154] << 8) + data[1153]); + return 0; + } else { + midi_mt32_poke(0x200000, (guint8 *)" FreeSCI Rocks! ", 20); + return midi_mt32_defaults(0x0c,1); /* send defaults in absence of patch data */ + } + return -1; +} + +int midi_mt32_close(void) +{ + midi_mt32_allstop(); + if (type == 0) { + printf("MT-32: Displaying Text: \"%.20s\"\n", shutdown_msg); + midi_mt32_poke(0x200000, (unsigned char *) shutdown_msg, 20); + } + midi_writer->close(midi_writer); + return SFX_OK; +} + +int midi_mt32_volume(guint8 volume) +{ + volume &= 0x7f; /* (make sure it's not over 127) */ + if (midi_mt32_poke(0x100016, &volume, 1) < 0) + return -1; + + return 0; +} + +int midi_mt32_allstop(void) +{ + byte buf[4]; + int i; + + buf[0] = 0x7b; + buf[1] = 0; + buf[2] = 0; + + for (i = 0; i < 16; i++) { + midi_mt32_event((guint8)(0xb0 | i), 2, buf); + } + + return 0; +} + +int midi_mt32_reverb(int param) +{ + guint8 buffer[3]; + + if (param == -1) + param = default_reverb; + + printf("MT-32: Sending reverb # %d (%d, %d, %d)\n",param, mt32_reverb[param].mode, + mt32_reverb[param].time, + mt32_reverb[param].level); + + buffer[0] = mt32_reverb[param].mode; + buffer[1] = mt32_reverb[param].time; + buffer[2] = mt32_reverb[param].level; + midi_mt32_poke(0x100001, buffer, 3); + + return 0; +} + + +static int +midi_mt32_poke(guint32 address, guint8 *data, unsigned int count) +{ + guint8 checksum = 0; + unsigned int i; + + if (count > 256) return -1; + + checksum -= (sysex_buffer[5] = (char)((address >> 16) & 0x7F)); + checksum -= (sysex_buffer[6] = (char)((address >> 8) & 0x7F)); + checksum -= (sysex_buffer[7] = (char)(address & 0x7F)); + + for (i = 0; i < count; i++) + checksum -= (sysex_buffer[i + 8] = data[i]); + + sysex_buffer[count + 8] = checksum & 0x7F; + sysex_buffer[count + 9] = 0xF7; + + midiout_write_delayed_block(sysex_buffer, count + 10); + if (midi_writer->flush) + midi_writer->flush(midi_writer); + midi_mt32_sysex_delay(); + + return count + 10; + +} + +static int +midi_mt32_poke_gather(guint32 address, guint8 *data1, unsigned int count1, + guint8 *data2, unsigned int count2) +{ + guint8 checksum = 0; + unsigned int i; + + if ((count1 + count2) > 256) return -1; + + checksum -= (sysex_buffer[5] = (char)((address >> 16) & 0x7F)); + checksum -= (sysex_buffer[6] = (char)((address >> 8) & 0x7F)); + checksum -= (sysex_buffer[7] = (char)(address & 0x7F)); + + for (i = 0; i < count1; i++) + checksum -= (sysex_buffer[i + 8] = data1[i]); + for (i = 0; i < count2; i++) + checksum -= (sysex_buffer[i + 8 + count1] = data2[i]); + + sysex_buffer[count1 + count2 + 8] = checksum & 0x7F; + sysex_buffer[count1 + count2 + 9] = 0xF7; + + midiout_write_delayed_block(sysex_buffer, count1 + count2 + 10); + if (midi_writer->flush) + midi_writer->flush(midi_writer); + midi_mt32_sysex_delay(); + return count1 + count2 + 10; +} + + +static int +midi_mt32_write_block(guint8 *data, unsigned int count) +{ + unsigned int block_start = 0; + unsigned int i = 0; + + while (i < count) { + if ((data[i] == 0xF0) && (i != block_start)) { + midiout_write_delayed_block(data + block_start, i - block_start); + block_start = i; + } + if (data[i] == 0xF7) { + midiout_write_delayed_block(data + block_start, i - block_start + 1); + midi_mt32_sysex_delay(); + block_start = i + 1; + } + i++; + } + if (count >= block_start) { + if (midiout_write_delayed_block(data + block_start, count - block_start + ) != (count - block_start)) { + fprintf(stderr, "midi_mt32_write_block(): midiout_write_block failed!\n"); + return 1; + } + } + + return 0; +} + +static int +midi_mt32_sysex_delay(void) +{ + /* Under Win32, we won't get any sound, in any case... */ +#ifdef HAVE_USLEEP + usleep(320 * 63); /* One MIDI byte is 320us, 320us * 63 > 20ms */ +#elif defined (_WIN32) + Sleep(((320 * 63) / 1000) + 1); +#elif defined (__BEOS__) + snooze(320 * 63); +#else + sleep(1); +#endif + return 0; +} + +static int +midi_mt32_event(byte command, int argc, byte *argv) +{ + byte buf[8]; + + buf[0] = command; + memcpy(buf + 1, argv, argc); + + midiout_write_block(buf, argc + 1, delta); + delta = 0; + + return SFX_OK; +} + + +static void +delay_init(void) +{/* Wait for MT-32 initialisation to complete */ + long endsec = mt32_init_sec, uendsec = mt32_init_usec; + long sec, usec; + int loopcount = 0; + + uendsec += (mt32_init_delay * 100000) / 6; /* mt32_init_delay is in ticks (1/60th seconds), uendsecs in microseconds */ + endsec += uendsec / 1000000; + uendsec %= 1000000; + + + do { + if (loopcount == 1) + sciprintf("Waiting for MT-32 programming to complete...\n"); + + sci_gettime(&sec, &usec); + sleep(1); /* Idle a bit */ + ++loopcount; + } while ((sec < endsec) || ((sec == endsec) && (usec < uendsec))); + +} + +static int +midi_mt32_reset_timer(GTimeVal ts) +{ + if (mt32_init_delay) { /* We might still have to wait for initialisation to complete */ + delay_init(); + mt32_init_delay = 0; + } + + + midi_writer->reset_timer(midi_writer); + return SFX_OK; +} + + +static int +midi_mt32_delay(int ticks) +{ + delta += ticks; /* Accumulate, write before next command */ + return SFX_OK; +} + +static int +midi_mt32_set_option(char *name, char *value) +{ + return SFX_ERROR; /* No options are supported at this time */ +} + +/* the driver struct */ + +sfx_sequencer_t sfx_sequencer_mt32 = { + "MT32", + "0.1", + SFX_DEVICE_MIDI, /* No device dependancy-- fixme, this might becomde ossseq */ + &midi_mt32_set_option, + &midi_mt32_open, + &midi_mt32_close, + &midi_mt32_event, + &midi_mt32_delay, + &midi_mt32_reset_timer, + &midi_mt32_allstop, + &midi_mt32_volume, + &midi_mt32_reverb, + 001, /* patch.001 */ + SFX_SEQ_PATCHFILE_NONE, + 0x01, /* playflag */ + 1, /* do play channel 9 */ + 32, /* Max polyphony */ + 0 /* Does not require any write-ahead by its own */ +}; diff --git a/engines/sci/sfx/seq/oss-adlib.c b/engines/sci/sfx/seq/oss-adlib.c new file mode 100644 index 0000000000..0406556f56 --- /dev/null +++ b/engines/sci/sfx/seq/oss-adlib.c @@ -0,0 +1,374 @@ +/*************************************************************************** + oss-adlib.c Copyright (C) 2001 Solomon Peachy, 03,04 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#include +#ifdef HAVE_UNISTD_H +# include +#endif +#include "../sequencer.h" + +#ifdef HAVE_SYS_SOUNDCARD_H + +#include "../adlib.h" + +#include +#include +#include +#include /* for some MIDI information */ + +#if 1 +SEQ_DEFINEBUF(2048); +static int seqfd; +#else +extern unsigned char _seqbuf[2048]; +extern int _seqbuflen; +extern int _seqbufptr; +extern int seqfd; +#endif + +static guint8 instr[MIDI_CHANNELS]; +static int dev; +static int free_voices = ADLIB_VOICES; +static long note_time[ADLIB_VOICES]; +static unsigned char oper_note[ADLIB_VOICES]; +static unsigned char oper_chn[ADLIB_VOICES]; + +#if 1 +void seqbuf_dump(void) /* OSS upcall */ +{ + if (_seqbufptr) + if (write(seqfd, _seqbuf, _seqbufptr) == -1) { + perror("ADLIB write "); + exit(-1); + } + _seqbufptr = 0; +} +#endif + +/* initialise note/operator lists, etc. */ +void adlib_init_lists(void) +{ + int i; + for(i = 0 ; i < ADLIB_VOICES ; i++) { + oper_note[i] = 255; + oper_chn[i] = 255; + note_time[i] = 0; + } + free_voices = ADLIB_VOICES; +} + +int adlib_stop_note(int chn, int note, int velocity) +{ + int i, op=255; + + for (i=0;i= ADLIB_VOICES) { + printf("Free list empty but no notes playing\n"); + return 255; + } /* No notes playing */ + + for (i = 0; i < ADLIB_VOICES ; i++) { + if (oper_chn[i] != chn) + continue; + if (note_time[i] == 0) + continue; + if (time == 0) { + time = note_time[i]; + oldest = i; + continue; + } + if (note_time[i] < time) { + time = note_time[i]; + oldest = i; + } + } + + /* printf("Killing chn %d, oper %d\n", chn, oldest); */ + + if (oldest == 255) + return 255; /* Was already stopped. Why? */ + + SEQ_STOP_NOTE(dev, oldest, oper_note[oldest], 0); + SEQ_DUMPBUF(); + + oper_chn[oldest] = 255; + oper_note[oldest] = 255; + note_time[oldest] = 0; + free_voices++; + + return oldest; +} + +static void +adlib_start_note(int chn, int note, int velocity) +{ + int free; + struct timeval now; + + if (velocity == 0) { + adlib_stop_note(chn, note, velocity); + return; + } + + gettimeofday(&now, NULL); + + if (free_voices <= 0) + free = adlib_kill_one_note(chn); + else + for (free = 0; free < ADLIB_VOICES ; free++) + if (oper_chn[free] == 255) + break; + + /* printf("play operator %d/%d: %d %d %d\n", free, free_voices, chn, note, velocity); */ + + oper_chn[free] = chn; + oper_note[free] = note; + note_time[free] = now.tv_sec * 1000000 + now.tv_usec; + free_voices--; + + SEQ_SET_PATCH(dev, free, instr[chn]); + SEQ_START_NOTE(dev, free, note, velocity); + SEQ_DUMPBUF(); +} + +static int +midi_adlib_open(int data_length, byte *data_ptr, int data2_length, + byte *data2_ptr, void *seq) +{ + int nrdevs, i, n; + struct synth_info info; + struct sbi_instrument sbi; + + if (data_length < 1344) { + printf ("invalid patch.003"); + return -1; + } + + for (i = 0; i < 48; i++) + make_sbi((adlib_def *)(data_ptr+(28 * i)), adlib_sbi[i]); + + if (data_length > 1344) + for (i = 48; i < 96; i++) + make_sbi((adlib_def *)(data_ptr+2+(28 * i)), adlib_sbi[i]); + + memset(instr, 0, sizeof(instr)); + + if (!IS_VALID_FD(seqfd=open("/dev/sequencer", O_WRONLY, 0))) { + perror("/dev/sequencer"); + return(-1); + } + if (ioctl(seqfd, SNDCTL_SEQ_NRSYNTHS, &nrdevs) == -1) { + perror("/dev/sequencer"); + return(-1); + } + for (i=0;i 1) + return midi_adlib_event1(command, argv[0], argv[1]); + else + return midi_adlib_event2(command, argv[0]); +} + +static int +midi_adlib_delay(int ticks) +{ + SEQ_DELTA_TIME(ticks); + return SFX_OK; +} + +static int +midi_adlib_set_option(char *name, char *value) +{ + return SFX_ERROR; /* No options are supported at this time */ +} + +/* the driver struct */ + +sfx_sequencer_t sfx_sequencer_oss_adlib = { + "adlib", + "0.1", + SFX_DEVICE_NONE, /* No device dependancy-- fixme, this might become ossseq */ + &midi_adlib_set_option, + &midi_adlib_open, + &midi_adlib_close, + &midi_adlib_event, + &midi_adlib_delay, + NULL, + &midi_adlib_allstop, + NULL, + &midi_adlib_reverb, + 003, /* patch.003 */ + SFX_SEQ_PATCHFILE_NONE, + 0x04, /* playflag */ + 0, /* do not play channel 9 */ + ADLIB_VOICES, /* Max polyphony */ + 0 /* Does not require any write-ahead by its own */ +}; + +#endif /* HAVE_SYS_SOUNDCARD_H */ diff --git a/engines/sci/sfx/seq/sequencers.c b/engines/sci/sfx/seq/sequencers.c new file mode 100644 index 0000000000..cb43381fa9 --- /dev/null +++ b/engines/sci/sfx/seq/sequencers.c @@ -0,0 +1,68 @@ +/*************************************************************************** + sequencers.c Copyright (C) 2004 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include "../sequencer.h" +#include + +#ifndef SCUMMVM +extern sfx_sequencer_t sfx_sequencer_gm; +extern sfx_sequencer_t sfx_sequencer_mt32; +#ifdef HAVE_SYS_SOUNDCARD_H +extern sfx_sequencer_t sfx_sequencer_oss_adlib; +#endif +#endif // SCUMMVM + +sfx_sequencer_t *sfx_sequencers[] = { +#ifndef SCUMMVM + &sfx_sequencer_gm, + &sfx_sequencer_mt32, +#ifdef HAVE_SYS_SOUNDCARD_H + &sfx_sequencer_oss_adlib, +#endif +#endif // SCUMMVM + NULL +}; + + +sfx_sequencer_t * +sfx_find_sequencer(char *name) +{ + if (!name) { + /* Implement default policy for your platform (if any) here, or in a function + ** called from here (if it's non-trivial). Try to use midi_devices[0], if + ** feasible. */ + + return sfx_sequencers[0]; /* default */ + } else { + int n = 0; + while (sfx_sequencers[n] + && strcasecmp(sfx_sequencers[n]->name, name)) + ++n; + + return sfx_sequencers[n]; + } +} diff --git a/engines/sci/sfx/sequencer.h b/engines/sci/sfx/sequencer.h new file mode 100644 index 0000000000..586e4b3e51 --- /dev/null +++ b/engines/sci/sfx/sequencer.h @@ -0,0 +1,142 @@ +/*************************************************************************** + sfx_sequencer.h, from + midi_device.h Copyright (C) 2001 Solomon Peachy + Copytight (C) 2002 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + + +#ifndef _SFX_SEQUENCER_H_ +#define _SFX_SEQUENCER_H_ + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ +#include +#include +#include "device.h" +#include + +#define SFX_SEQ_PATCHFILE_NONE -1 + +typedef struct _sfx_sequencer { + const char *name; /* Sequencer name */ + const char *version; /* Sequencer version */ + + int device; /* Type of device the sequencer depends on, may be SFX_DEVICE_NONE. */ + + int + (*set_option)(char *name, char *value); + /* Sets an option for the sequencing mechanism + ** Parameters: (char *) name: The name describing what to set + ** (char *) value: The value to set + ** Returns : (int) SFX_OK, or SFX_ERROR if the name wasn't understood + */ + + int + (*open)(int patch_len, byte *patch, int patch2_len, byte *patch2, void *device); + /* Opens the sequencer for writing + ** Parameters: (int) patch_len, patch2_len: Length of the patch data + ** (byte *) patch, patch2: Bulk patch data + ** (void *) device: A device matching the 'device' property, or NULL + ** if the property is null. + ** Returns : (int) SFX_OK on success, SFX_ERROR otherwise + ** The device should be initialized to a tick frequency of 60 Hz. + ** 'patch' and 'patch_len' refer to the patch resource passed to open, + ** as specified by the 'patchfile' property. 'patch' may be NULL if the + ** resource wasn't found. + ** For more information regarding patch resources, please refer to the + ** FreeSCI documentation, particularly the part regarding 'patch.*' resource + ** data. + */ + + int (*close)(void); + /* Closes the sequencer + ** Returns : SFX_OK on success, SFX_ERROR otherwise + */ + + int (*event)(byte command, int argc, byte *argv); + /* Plays a MIDI event + ** Parameters: (byte) command: MIDI command to play + ** (int) argc: Number of arguments to the command + ** (byte *) argv: Pointer to additional arguments + ** Returns : SFX_OK on success, SFX_ERROR otherwise + ** argv is guaranteed to point to a sufficiently large number of + ** arguments, as indicated by 'command' and the MIDI standard. + ** No 'running status' will be passed, 'command' will always be + ** explicit. + */ + int (*delay)(int ticks); /* OPTIONAL -- may be NULL, but highly recommended */ + /* Inserts a delay (delta time) into the sequencer cue + ** Parameters: (int) ticks: Number of 60 Hz ticks to delay + ** Returns : SFX_OK on success, SFX_ERROR otherwise + */ + + int (*reset_timer)(GTimeVal ts); + /* OPTIONAL -- may be NULL, but highly recommended in combination with delay() */ + /* Resets the timer counter associated with the 'delay()' function + ** Parameters: (GTimeVal) ts: Timestamp of the base time + ** Returns : SFX_OK on success, SFX_ERROR otherwise + */ + + int (*allstop)(void); /* OPTIONAL -- may be NULL */ + /* Stops playing everything in the sequencer queue + ** Returns : SFX_OK on success, SFX_ERROR otherwise + */ + + int (*volume)(guint8 volume); /* OPTIONAL -- can be NULL */ + /* Sets the sequencer volume + ** Parameters; (byte) volume: The volume to set, with 0 being mute and 127 full volume + ** Returns : SFX_OK on success, SFX_ERROR otherwise + */ + + int (*reverb)(int param); /* OPTIONAL -- may be NULL */ + /* Sets the device reverb + ** Parameters; (int) param: The reverb to set + ** Returns : SFX_OK on success, SFX_ERROR otherwise + */ + + int patchfile, patchfile2; /* Patch resources to pass into the call to open(), + ** if present, or SFX_SEQ_PATCHFILE_NONE */ + guint8 playmask; /* SCI 'playflag' mask to determine which SCI song channels + ** this sequencer should play */ + /* 0x01 -- MT-32 + ** 0x02 -- Yamaha FB-01 + ** 0x04 -- CMS or Game Blaster + ** 0x08 -- Casio MT540 or CT460 + ** 0x10 -- Tandy 3-voice + ** 0x20 -- PC speaker + */ + guint8 play_rhythm; /* Plays the rhythm channel? */ + gint8 polyphony; /* Device polyphony (# of voices) */ + + int min_write_ahead_ms; /* Minimal write-ahead, in milliseconds */ + /* Note that write-ahead is tuned automatically; this enforces a lower limit */ + +} sfx_sequencer_t; + + +sfx_sequencer_t * +sfx_find_sequencer(char *name); +/* Finds a sequencer by name +** Parameters: (char *) name: Name of the sequencer to look up, or NULL for default +** Returns : (sfx_sequencer_t *) The sequencer of matching name, or NULL +** if not found +*/ + + +#endif /* _SFX_SEQUENCER_H_ */ diff --git a/engines/sci/sfx/softseq.h b/engines/sci/sfx/softseq.h new file mode 100644 index 0000000000..38efac6d59 --- /dev/null +++ b/engines/sci/sfx/softseq.h @@ -0,0 +1,134 @@ +/*************************************************************************** + sfx_softseq.h Copyright (C) 2004 Christoph Reichenbach + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + + Please contact the maintainer for any program-related bug reports or + inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef SFX_SOFTSEQ_H_ +#define SFX_SOFTSEQ_H_ + +#include +#include +#include "sequencer.h" +#include + + +/* Software sequencer */ +typedef struct sfx_softseq { + const char *name; + const char *version; + + int + (*set_option)(struct sfx_softseq *self, char *name, char *value); + /* Sets an option for the sequencer + ** Parameters: (sfx_softseq_t *) self: Self reference + ** (char *) name: Name of the option to set + ** (char *0 value: Value to set the option to + ** Returns : (int) GFX_OK on success, or GFX_ERROR if not supported + */ + + int + (*init)(struct sfx_softseq *self, byte *res_data, int res_size, + byte *res2_data, int res2_size); + /* Initialises the sequencer + ** Parameters: (sfx_softseq_t *) self: Self reference + ** (byte *) res_data: Resource data for 'patch_nr' (see below) + ** (int) res_size: Number of bytes in 'res_data' + ** (byte *) res2_data: Resource data for 'patch2_nr' (see below) + ** (int) res2_size: Number of bytes in 'res2_data' + ** Returns : (int) SFX_OK on success, SFX_ERROR otherwise + ** Note that 'res_data' is only a valid pointer for this call. If the + ** data is needed later during execution, it should be backed up internally. + ** If the requested resource is not available, res_data will be NULL + ** /even if/ patch_nr is set. + */ + + void + (*exit)(struct sfx_softseq *self); + /* Uninitialises the sequencer and frees all used resources + ** Parameters: (sfx_softseq_t *) self: Self reference + */ + + void + (*set_volume)(struct sfx_softseq *self, int new_volume); + /* Sets the sequencer volume + ** Parameters: (sfx_softseq_t *) self: Self reference + ** (int) new_volume: A volume, between 0 (quiet) and 127 (max) + */ + + void + (*handle_command)(struct sfx_softseq *self, byte cmd, int argc, byte *argv); + /* Handle a MIDI command + ** Parameters: (sfx_softseq_t *) self: Self reference + ** (byte) cmd: Basic MIDI command, always includes command and channel + ** (int) argc: Number of additional arguments to this command + ** (byte *) argv: Additional arguments to 'cmd' + */ + + void + (*poll)(struct sfx_softseq *self, byte *dest, int len); + /* Asks the software sequencer to fill in parts of a buffer + ** Parameters: (sfx_softseq_t *) self: Self reference + ** (int) len: Number of _frames_ to write + ** Returns : (byte) *dest: 'len' frames must be written to this buffer + */ + + void + (*allstop)(struct sfx_softseq *self); + /* Stops all sound generation + ** Parameters: (sfx_softseq_t *) self: Self reference + */ + + void *internal; /* Internal data, may be used by sfx_softseq_t inmplementors */ + + int patch_nr; /* Number of the first patch file associated with this sequencer, + ** or SFX_SEQ_PATCHFILE_NONE */ + int patch2_nr; /* Number of the second patch file associated with this sequencer, + ** or SFX_SEQ_PATCHFILE_NONE */ + int playmask; /* playflag identifying the device emulated */ + /* 0x01 -- MT-32 + ** 0x02 -- Yamaha FB-01 + ** 0x04 -- CMS or Game Blaster + ** 0x08 -- Casio MT540 or CT460 + ** 0x10 -- Tandy 3-voice + ** 0x20 -- PC speaker + */ + int play_rhythm; /* Whether the rhythm channel (9) should be played */ + int polyphony; /* Number of voices played */ + + sfx_pcm_config_t pcm_conf; /* Setup of the channel the sequencer writes to */ + +} sfx_softseq_t; + + +sfx_softseq_t * +sfx_find_softseq(char *name); +/* Finds a given or default software sequencer +** Parameters: (char *) name: Name of the sequencer to look up, or NULL for default +** Returns : (sfx_softseq_t *) The requested sequencer, or NULL if not found +*/ + +#endif /* !defined(SFX_SOFTSEQ_H_) */ diff --git a/engines/sci/sfx/softseq/Makefile.am b/engines/sci/sfx/softseq/Makefile.am new file mode 100644 index 0000000000..af7f8ca2c9 --- /dev/null +++ b/engines/sci/sfx/softseq/Makefile.am @@ -0,0 +1,5 @@ +noinst_LIBRARIES = libscisoftseq.a +INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@ +libscisoftseq_a_SOURCES = softsequencers.c pcspeaker.c SN76496.c fmopl.c opl2.c amiga.c \ + fluidsynth.c +EXTRA_DIST = fmopl.h diff --git a/engines/sci/sfx/softseq/SN76496.c b/engines/sci/sfx/softseq/SN76496.c new file mode 100644 index 0000000000..cb32b89f0c --- /dev/null +++ b/engines/sci/sfx/softseq/SN76496.c @@ -0,0 +1,248 @@ +/*************************************************************************** + SN76496.c Copyright (C) 2004 Christoph Reichenbach + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + + Please contact the maintainer for any program-related bug reports or + inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Tandy/PCJr sequencer for FreeSCI */ + +#include "../softseq.h" +#include + +#define FREQUENCY 44100 +#define CHANNELS_NR 3 +#define VOLUME_SHIFT 3 + +static int global_volume = 100; /* Base volume */ +static int volumes[CHANNELS_NR] = { 100, 100, 100 }; +static int notes[CHANNELS_NR] = {0, 0, 0}; /* Current halftone, or 0 if off */ +static int freq_count[CHANNELS_NR] = {0, 0, 0}; +static int channel_assigner = 0; +static int channels_assigned = 0; +static int chan_nrs[CHANNELS_NR] = {-1, -1, -1}; + +extern sfx_softseq_t sfx_softseq_pcspeaker; +/* Forward-declare the sequencer we are defining here */ + + +static int +SN76496_set_option(sfx_softseq_t *self, char *name, char *value) +{ + return SFX_ERROR; +} + +static int +SN76496_init(sfx_softseq_t *self, byte *patch, int patch_len, byte *patch2, + int patch2_len) +{ + return SFX_OK; +} + +static void +SN76496_exit(sfx_softseq_t *self) +{ +} + +static void +SN76496_event(sfx_softseq_t *self, byte command, int argc, byte *argv) +{ + int i; + int chan = -1; +#if 0 + fprintf(stderr, "Note [%02x : %02x %02x]\n", command, argc?argv[0] : 0, (argc > 1)? argv[1] : 0); +#endif + if ((command & 0xe0) == 0x80) { + int chan_nr = command & 0xf; + + /* First, test for channel having been assigned already */ + if (channels_assigned & (1 << chan_nr)) { + /* Already assigned this channel number: */ + for (i = 0; i < CHANNELS_NR; i++) + if (chan_nrs[i] == chan_nr) { + chan = i; + break; + } + } else { + /* Assign new channel round-robin */ + + /* Mark channel as unused: */ + if (chan_nrs[channel_assigner] >= 0) + channels_assigned &= ~(1 << chan_nrs[channel_assigner]); + + /* Remember channel: */ + chan_nrs[channel_assigner] = chan_nr; + /* Mark channel as used */ + channels_assigned |= (1 << chan_nrs[channel_assigner]); + + /* Save channel for use later in this call: */ + chan = channel_assigner; + /* Round-ropin iterate channel assigner: */ + channel_assigner = (channel_assigner + 1) % CHANNELS_NR; + } + } +#if 0 + fprintf(stderr, " --> %d [%04x], {%d,%d,%d}@%d\n", chan, + channels_assigned, chan_nrs[0],chan_nrs[1],chan_nrs[2],channel_assigner); +#endif + + switch (command & 0xf0) { + + case 0x80: + if (argv[0] == notes[chan]) + notes[chan] = 0; + break; + + case 0x90: + if (!argv[1]) { + if (argv[chan] == notes[chan]) + notes[chan] = 0; + } else { + notes[chan] = argv[0]; + volumes[chan] = argv[1]; + } + break; + + case 0xb0: + if (argv[1] == SCI_MIDI_CHANNEL_NOTES_OFF) + notes[chan] = 0; + break; + + + default: +#if DEBUG + fprintf(stderr, "[SFX:PCM-PC] Unused MIDI command %02x %02x %02x\n", command, argc?argv[0] : 0, (argc > 1)? argv[1] : 0); +#endif + break; /* ignore */ + } +} + +#define BASE_NOTE 129 /* A10 */ +#define BASE_OCTAVE 10 /* A10, as I said */ + +static int +freq_table[12] = { /* A4 is 440Hz, halftone map is x |-> ** 2^(x/12) */ + 28160, /* A10 */ + 29834, + 31608, + 33488, + 35479, + 37589, + 39824, + 42192, + 44701, + 47359, + 50175, + 53159 +}; + +static inline int +get_freq(int note) +{ + int halftone_delta = note - BASE_NOTE; + int oct_diff = ((halftone_delta + BASE_OCTAVE * 12) / 12) - BASE_OCTAVE; + int halftone_index = (halftone_delta + (12*100)) % 12 ; + int freq = (!note)? 0 : freq_table[halftone_index] / (1 << (-oct_diff)); + + return freq; +} + + +void +SN76496_poll(sfx_softseq_t *self, byte *dest, int len) +{ + gint16 *buf = (gint16 *) dest; + int i; + int chan; + int freq[CHANNELS_NR]; + + for (chan = 0; chan < CHANNELS_NR; chan++) + freq[chan] = get_freq(notes[chan]); + + for (i = 0; i < len; i++) { + int result = 0; + + for (chan = 0; chan < CHANNELS_NR; chan++) + if (notes[chan]) { + int volume = (global_volume * volumes[chan]) + >> VOLUME_SHIFT; + + freq_count[chan] += freq[chan]; + while (freq_count[chan] >= (FREQUENCY << 1)) + freq_count[chan] -= (FREQUENCY << 1); + + if (freq_count[chan] - freq[chan] < 0) { + /* Unclean rising edge */ + int l = volume << 1; + result += -volume + (l*freq_count[chan])/freq[chan]; + } else if (freq_count[chan] >= FREQUENCY + && freq_count[chan] - freq[chan] < FREQUENCY) { + /* Unclean falling edge */ + int l = volume << 1; + result += volume - (l*(freq_count[chan] - FREQUENCY))/freq[chan]; + } else { + if (freq_count[chan] < FREQUENCY) + result += volume; + else + result += -volume; + } + } + buf[i] = result; + } + +} + +void +SN76496_allstop(sfx_softseq_t *self) +{ + int i; + for (i = 0; i < CHANNELS_NR; i++) + notes[i] = 0; +} + +void +SN76496_volume(sfx_softseq_t *self, int new_volume) +{ + global_volume = new_volume; +} + + +sfx_softseq_t sfx_softseq_SN76496 = { + "SN76496", + "0.1", + SN76496_set_option, + SN76496_init, + SN76496_exit, + SN76496_volume, + SN76496_event, + SN76496_poll, + SN76496_allstop, + NULL, + SFX_SEQ_PATCHFILE_NONE, + SFX_SEQ_PATCHFILE_NONE, + 0x10, /* Tandy/PCJr channels */ + 0, /* No rhythm channel */ + 3, /* # of voices */ + {FREQUENCY, SFX_PCM_MONO, SFX_PCM_FORMAT_S16_NATIVE} +}; diff --git a/engines/sci/sfx/softseq/amiga.c b/engines/sci/sfx/softseq/amiga.c new file mode 100644 index 0000000000..12240897d7 --- /dev/null +++ b/engines/sci/sfx/softseq/amiga.c @@ -0,0 +1,658 @@ +/*************************************************************************** + amiga.c Copyright (C) 2007 Walter van Niftrik + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + + Please contact the maintainer for any program-related bug reports or + inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +#include "resource.h" +#include "sci_memory.h" +#include "../softseq.h" + +#define FREQUENCY 44100 +#define CHANNELS_NR 10 +#define HW_CHANNELS_NR 16 + +/* Samplerate of the instrument bank */ +#define BASE_FREQ 20000 + +/* Instrument looping flag */ +#define MODE_LOOP 1 << 0 +/* Instrument pitch changes flag */ +#define MODE_PITCH 1 << 1 + +#define PAN_LEFT 91 +#define PAN_RIGHT 164 + +/* #define DEBUG */ + +typedef struct envelope { + /* Phase period length in samples */ + int length; + /* Velocity delta per period */ + int delta; + /* Target velocity */ + int target; +} envelope_t; + +/* Fast decay envelope */ +static envelope_t env_decay = {FREQUENCY / (32 * 64), 1, 0}; + +typedef struct instrument { + char name[30]; + int mode; + /* Size of non-looping part in bytes */ + int size; + /* Starting offset and size of loop in bytes */ + int loop_size; + /* Transpose value in semitones */ + int transpose; + /* Envelope */ + envelope_t envelope[4]; + sbyte *samples; + sbyte *loop; +} instrument_t; + +typedef struct bank { + char name[30]; + int size; + instrument_t *instruments[256]; +} bank_t; + +typedef struct channel { + int instrument; + int note; + int note_velocity; + int velocity; + int envelope; + /* Number of samples till next envelope event */ + int envelope_samples; + int decay; + int looping; + int hw_channel; + frac_t offset; + frac_t rate; +} channel_t; + +typedef struct hw_channel { + int instrument; + int volume; + int pan; +} hw_channel_t; + +/* Instrument bank */ +static bank_t bank; +/* Internal channels */ +static channel_t channels[CHANNELS_NR]; +/* External channels */ +static hw_channel_t hw_channels[HW_CHANNELS_NR]; +/* Overall volume */ +static int volume = 127; + +/* Frequencies for every note */ +static int freq_table[] = { + 58, 62, 65, 69, 73, 78, 82, 87, + 92, 98, 104, 110, 117, 124, 131, 139, + 147, 156, 165, 175, 185, 196, 208, 220, + 234, 248, 262, 278, 294, 312, 331, 350, + 371, 393, 417, 441, 468, 496, 525, 556, + 589, 625, 662, 701, 743, 787, 834, 883, + 936, 992, 1051, 1113, 1179, 1250, 1324, 1403, + 1486, 1574, 1668, 1767, 1872, 1984, 2102, 2227, + 2359, 2500, 2648, 2806, 2973, 3149, 3337, 3535, + 3745, 3968, 4204, 4454, 4719, 5000, 5297, 5612, + 5946, 6299, 6674, 7071, 7491, 7937, 8408, 8908, + 9438, 10000, 10594, 11224, 11892, 12599, 13348, 14142, + 14983, 15874, 16817, 17817, 18877, 20000, 21189, 22449, + 23784, 25198, 26696, 28284, 29966, 31748, 33635, 35635, + 37754, 40000, 42378, 44898, 47568, 50396, 53393, 56568, + 59932, 63496, 67271, 71271, 75509, 80000, 84757, 89796 +}; + +static void +set_envelope(channel_t *channel, envelope_t *envelope, int phase) +{ + channel->envelope = phase; + channel->envelope_samples = envelope[phase].length; + + if (phase == 0) + channel->velocity = channel->note_velocity / 2; + else + channel->velocity = envelope[phase - 1].target; +} + +static inline int +interpolate(sbyte *samples, frac_t offset) +{ + int x = frac_to_int(offset); + int diff = (samples[x + 1] - samples[x]) << 8; + + return (samples[x] << 8) + frac_to_int(diff * (offset & FRAC_LO_MASK)); +} + +static void +play_instrument(gint16 *dest, channel_t *channel, int count) +{ + int index = 0; + int vol = hw_channels[channel->hw_channel].volume; + instrument_t *instrument = bank.instruments[channel->instrument]; + + while (1) { + /* Available source samples until end of segment */ + frac_t lin_avail; + int seg_end, rem, i, amount; + sbyte *samples; + + if (channel->looping) { + samples = instrument->loop; + seg_end = instrument->loop_size; + } + else { + samples = instrument->samples; + seg_end = instrument->size; + } + + lin_avail = int_to_frac(seg_end) - channel->offset; + + rem = count - index; + + /* Amount of destination samples that we will compute this iteration */ + amount = lin_avail / channel->rate; + + if (lin_avail % channel->rate) + amount++; + + if (amount > rem) + amount = rem; + + /* Stop at next envelope event */ + if ((channel->envelope_samples != -1) && (amount > channel->envelope_samples)) + amount = channel->envelope_samples; + + for (i = 0; i < amount; i++) { + dest[index++] = interpolate(samples, channel->offset) * channel->velocity / 64 * channel->note_velocity * vol / (127 * 127); + channel->offset += channel->rate; + } + + if (channel->envelope_samples != -1) + channel->envelope_samples -= amount; + + if (channel->envelope_samples == 0) { + envelope_t *envelope; + int delta, target, velocity; + + if (channel->decay) + envelope = &env_decay; + else + envelope = &instrument->envelope[channel->envelope]; + + delta = envelope->delta; + target = envelope->target; + velocity = channel->velocity - envelope->delta; + + /* Check whether we have reached the velocity target for the current phase */ + if ((delta >= 0 && velocity <= target) || (delta < 0 && velocity >= target)) { + channel->velocity = target; + + /* Stop note after velocity has dropped to 0 */ + if (target == 0) { + channel->note = -1; + break; + } else + switch (channel->envelope) { + case 0: + case 2: + /* Go to next phase */ + set_envelope(channel, instrument->envelope, channel->envelope + 1); + break; + case 1: + case 3: + /* Stop envelope */ + channel->envelope_samples = -1; + break; + } + } else { + /* We haven't reached the target yet */ + channel->envelope_samples = envelope->length; + channel->velocity = velocity; + } + } + + if (index == count) + break; + + if (frac_to_int(channel->offset) >= seg_end) { + if (instrument->mode & MODE_LOOP) { + /* Loop the samples */ + channel->offset -= int_to_frac(seg_end); + channel->looping = 1; + } else { + /* All samples have been played */ + channel->note = -1; + break; + } + } + } +} + +static void +change_instrument(int channel, int instrument) +{ +#ifdef DEBUG + if (bank.instruments[instrument]) + sciprintf("[sfx:seq:amiga] Setting channel %i to \"%s\" (%i)\n", channel, bank.instruments[instrument]->name, instrument); + else + sciprintf("[sfx:seq:amiga] Warning: instrument %i does not exist (channel %i)\n", instrument, channel); +#endif + hw_channels[channel].instrument = instrument; +} + +static void +stop_channel(int ch) +{ + int i; + + /* Start decay phase for note on this hw channel, if any */ + for (i = 0; i < CHANNELS_NR; i++) + if (channels[i].note != -1 && channels[i].hw_channel == ch && !channels[i].decay) { + /* Trigger fast decay envelope */ + channels[i].decay = 1; + channels[i].envelope_samples = env_decay.length; + break; + } +} + +static void +stop_note(int ch, int note) +{ + int channel; + instrument_t *instrument; + + for (channel = 0; channel < CHANNELS_NR; channel++) + if (channels[channel].note == note && channels[channel].hw_channel == ch && !channels[channel].decay) + break; + + if (channel == CHANNELS_NR) { +#ifdef DEBUG + sciprintf("[sfx:seq:amiga] Warning: cannot stop note %i on channel %i\n", note, ch); +#endif + return; + } + + instrument = bank.instruments[channels[channel].instrument]; + + /* Start the envelope phases for note-off if looping is on and envelope is enabled */ + if ((instrument->mode & MODE_LOOP) && (instrument->envelope[0].length != 0)) + set_envelope(&channels[channel], instrument->envelope, 2); +} + +static void +start_note(int ch, int note, int velocity) +{ + instrument_t *instrument; + int channel; + + if (hw_channels[ch].instrument < 0 || hw_channels[ch].instrument > 255) { + sciprintf("[sfx:seq:amiga] Error: invalid instrument %i on channel %i\n", hw_channels[ch].instrument, ch); + return; + } + + instrument = bank.instruments[hw_channels[ch].instrument]; + + if (!instrument) { + sciprintf("[sfx:seq:amiga] Error: instrument %i does not exist\n", hw_channels[ch].instrument); + return; + } + + for (channel = 0; channel < CHANNELS_NR; channel++) + if (channels[channel].note == -1) + break; + + if (channel == CHANNELS_NR) { + sciprintf("[sfx:seq:amiga] Warning: could not find a free channel\n"); + return; + } + + stop_channel(ch); + + if (instrument->mode & MODE_PITCH) { + int fnote = note + instrument->transpose; + + if (fnote < 0 || fnote > 127) { + sciprintf("[sfx:seq:amiga] Error: illegal note %i\n", fnote); + return; + } + + /* Compute rate for note */ + channels[channel].rate = double_to_frac(freq_table[fnote] / (double) FREQUENCY); + } + else + channels[channel].rate = double_to_frac(BASE_FREQ / (double) FREQUENCY); + + channels[channel].instrument = hw_channels[ch].instrument; + channels[channel].note = note; + channels[channel].note_velocity = velocity; + + if ((instrument->mode & MODE_LOOP) && (instrument->envelope[0].length != 0)) + set_envelope(&channels[channel], instrument->envelope, 0); + else { + channels[channel].velocity = 64; + channels[channel].envelope_samples = -1; + } + + channels[channel].offset = 0; + channels[channel].hw_channel = ch; + channels[channel].decay = 0; + channels[channel].looping = 0; +} + +static gint16 read_int16(byte *data) +{ + return (data[0] << 8) | data[1]; +} + +static gint32 read_int32(byte *data) +{ + return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; +} + +static instrument_t *read_instrument(FILE *file, int *id) +{ + instrument_t *instrument; + byte header[61]; + int size; + int seg_size[3]; + int loop_offset; + int i; + + if (fread(header, 1, 61, file) < 61) { + sciprintf("[sfx:seq:amiga] Error: failed to read instrument header\n"); + return NULL; + } + + instrument = (instrument_t *) sci_malloc(sizeof(instrument_t)); + + seg_size[0] = read_int16(header + 35) * 2; + seg_size[1] = read_int16(header + 41) * 2; + seg_size[2] = read_int16(header + 47) * 2; + + instrument->mode = header[33]; + instrument->transpose = (gint8) header[34]; + for (i = 0; i < 4; i++) { + int length = (gint8) header[49 + i]; + + if (length == 0 && i > 0) + length = 256; + + instrument->envelope[i].length = length * FREQUENCY / 60; + instrument->envelope[i].delta = (gint8) header[53 + i]; + instrument->envelope[i].target = header[57 + i]; + } + /* Final target must be 0 */ + instrument->envelope[3].target = 0; + + loop_offset = read_int32(header + 37) & ~1; + size = seg_size[0] + seg_size[1] + seg_size[2]; + + *id = read_int16(header); + + strncpy(instrument->name, (char *) header + 2, 29); + instrument->name[29] = 0; +#ifdef DEBUG + sciprintf("[sfx:seq:amiga] Reading instrument %i: \"%s\" (%i bytes)\n", + *id, instrument->name, size); + sciprintf(" Mode: %02x\n", instrument->mode); + sciprintf(" Looping: %s\n", instrument->mode & MODE_LOOP ? "on" : "off"); + sciprintf(" Pitch changes: %s\n", instrument->mode & MODE_PITCH ? "on" : "off"); + sciprintf(" Segment sizes: %i %i %i\n", seg_size[0], seg_size[1], seg_size[2]); + sciprintf(" Segment offsets: 0 %i %i\n", loop_offset, read_int32(header + 43)); +#endif + instrument->samples = (sbyte *) sci_malloc(size + 1); + if (fread(instrument->samples, 1, size, file) < size) { + sciprintf("[sfx:seq:amiga] Error: failed to read instrument samples\n"); + return NULL; + } + + if (instrument->mode & MODE_LOOP) { + if (loop_offset + seg_size[1] > size) { +#ifdef DEBUG + sciprintf("[sfx:seq:amiga] Warning: looping samples extend %i bytes past end of sample block\n", + loop_offset + seg_size[1] - size); +#endif + seg_size[1] = size - loop_offset; + } + + if (seg_size[1] < 0) { + sciprintf("[sfx:seq:amiga] Error: invalid looping point\n"); + return NULL; + } + + instrument->size = seg_size[0]; + instrument->loop_size = seg_size[1]; + + instrument->loop = (sbyte*)sci_malloc(instrument->loop_size + 1); + memcpy(instrument->loop, instrument->samples + loop_offset, instrument->loop_size); + + instrument->samples[instrument->size] = instrument->loop[0]; + instrument->loop[instrument->loop_size] = instrument->loop[0]; + } else { + instrument->size = size; + instrument->samples[instrument->size] = 0; + } + + return instrument; +} + +static int +ami_set_option(sfx_softseq_t *self, char *name, char *value) +{ + return SFX_ERROR; +} + +static int +ami_init(sfx_softseq_t *self, byte *patch, int patch_len, byte *patch2, int patch2_len) +{ + FILE *file; + byte header[40]; + int i; + + file = sci_fopen("bank.001", "rb"); + + if (!file) { + sciprintf("[sfx:seq:amiga] Error: file bank.001 not found\n"); + return SFX_ERROR; + } + + if (fread(header, 1, 40, file) < 40) { + sciprintf("[sfx:seq:amiga] Error: failed to read header of file bank.001\n"); + fclose(file); + return SFX_ERROR; + } + + for (i = 0; i < 256; i++) + bank.instruments[i] = NULL; + + for (i = 0; i < CHANNELS_NR; i++) { + channels[i].note = -1; + } + + for (i = 0; i < HW_CHANNELS_NR; i++) { + hw_channels[i].instrument = -1; + hw_channels[i].volume = 127; + hw_channels[i].pan = (i % 4 == 0 || i % 4 == 3 ? PAN_LEFT : PAN_RIGHT); + } + + bank.size = read_int16(header + 38); + strncpy(bank.name, (char *) header + 8, 29); + bank.name[29] = 0; +#ifdef DEBUG + sciprintf("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", bank.size, bank.name); +#endif + + for (i = 0; i < bank.size; i++) { + int id; + instrument_t *instrument = read_instrument(file, &id); + + if (!instrument) { + sciprintf("[sfx:seq:amiga] Error: failed to read bank.001\n"); + fclose(file); + return SFX_ERROR; + } + + if (id < 0 || id > 255) { + sciprintf("[sfx:seq:amiga] Error: instrument ID out of bounds\n"); + return SFX_ERROR; + } + + bank.instruments[id] = instrument; + } + + fclose(file); + + return SFX_OK; +} + +static void +ami_exit(sfx_softseq_t *self) +{ + int i; + + for (i = 0; i < bank.size; i++) { + if (bank.instruments[i]) { + sci_free(bank.instruments[i]->samples); + sci_free(bank.instruments[i]); + } + } +} + +static void +ami_event(sfx_softseq_t *self, byte command, int argc, byte *argv) +{ + int channel, oper; + + channel = command & 0x0f; + oper = command & 0xf0; + + if (channel >= HW_CHANNELS_NR) { +#ifdef DEBUG + sciprintf("[sfx:seq:amiga] Warning: received event for non-existing channel %i\n", channel); +#endif + return; + } + + switch(oper) { + case 0x90: + if (argv[1] > 0) + start_note(channel, argv[0], argv[1]); + else + stop_note(channel, argv[0]); + break; + case 0xb0: + switch (argv[0]) { + case 0x07: + hw_channels[channel].volume = argv[1]; + break; + case 0x0a: +#ifdef DEBUG + sciprintf("[sfx:seq:amiga] Warning: ignoring pan 0x%02x event for channel %i\n", argv[1], channel); +#endif + break; + case 0x7b: + stop_channel(channel); + break; + default: + sciprintf("[sfx:seq:amiga] Warning: unknown control event 0x%02x\n", argv[0]); + } + break; + case 0xc0: + change_instrument(channel, argv[0]); + break; + default: + sciprintf("[sfx:seq:amiga] Warning: unknown event %02x\n", command); + } +} + +void +ami_poll(sfx_softseq_t *self, byte *dest, int len) +{ + int i, j; + gint16 *buf = (gint16 *) dest; + gint16 *buffers = (gint16*)malloc(len * 2 * CHANNELS_NR); + + memset(buffers, 0, len * 2 * CHANNELS_NR); + memset(dest, 0, len * 4); + + /* Generate samples for all notes */ + for (i = 0; i < CHANNELS_NR; i++) + if (channels[i].note >= 0) + play_instrument(buffers + i * len, &channels[i], len); + + for (j = 0; j < len; j++) { + int mixedl = 0, mixedr = 0; + + /* Mix and pan */ + for (i = 0; i < CHANNELS_NR; i++) { + mixedl += buffers[i * len + j] * (256 - hw_channels[channels[i].hw_channel].pan); + mixedr += buffers[i * len + j] * hw_channels[channels[i].hw_channel].pan; + } + + /* Adjust volume */ + buf[2 * j] = mixedl * volume >> 16; + buf[2 * j + 1] = mixedr *volume >> 16; + } +} + +void +ami_volume(sfx_softseq_t *self, int new_volume) +{ + volume = new_volume; +} + +void +ami_allstop(sfx_softseq_t *self) +{ + int i; + for (i = 0; i < HW_CHANNELS_NR; i++) + stop_channel(i); +} + +sfx_softseq_t sfx_softseq_amiga = { + "amiga", + "0.1", + ami_set_option, + ami_init, + ami_exit, + ami_volume, + ami_event, + ami_poll, + ami_allstop, + NULL, + SFX_SEQ_PATCHFILE_NONE, + SFX_SEQ_PATCHFILE_NONE, + 0x40, + 0, /* No rhythm channel (9) */ + HW_CHANNELS_NR, /* # of voices */ + {FREQUENCY, SFX_PCM_STEREO_LR, SFX_PCM_FORMAT_S16_NATIVE} +}; diff --git a/engines/sci/sfx/softseq/fluidsynth.c b/engines/sci/sfx/softseq/fluidsynth.c new file mode 100644 index 0000000000..a44d75e5bd --- /dev/null +++ b/engines/sci/sfx/softseq/fluidsynth.c @@ -0,0 +1,262 @@ +/*************************************************************************** + fluidsynth.c Copyright (C) 2008 Walter van Niftrik + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_FLUIDSYNTH_H + +#include + +#include "../softseq.h" +#include "../sequencer.h" +#include "../device.h" +#include "resource.h" + +static sfx_sequencer_t *gmseq; +static fluid_settings_t* settings; +static fluid_synth_t* synth; +static guint8 status; +static char *soundfont = "/etc/midi/8MBGMSFX.SF2"; +static int rpn[16]; + +#define SAMPLE_RATE 44100 +#define CHANNELS SFX_PCM_STEREO_LR + +/* MIDI writer */ + +static int +fluidsynth_midi_init(struct _midi_writer *self) +{ + return SFX_OK; +} + +static int +fluidsynth_midi_set_option(struct _midi_writer *self, char *name, char *value) +{ + return SFX_ERROR; +} + +static int +fluidsynth_midi_write(struct _midi_writer *self, unsigned char *buf, int len) +{ + if (buf[0] == 0xf0) + sciprintf("FluidSynth: Skipping sysex message.\n"); + else if (len == 2) { + guint8 command, channel; + + command = buf[0] & 0xf0; + channel = buf[0] & 0x0f; + + switch(command) { + case 0xc0: + fluid_synth_program_change(synth, channel, buf[1]); + break; + default: + printf("FluidSynth: MIDI command [%02x %02x] not supported\n", buf[0], buf[1]); + } + } + else if (len == 3) { + guint8 command, channel; + + command = buf[0] & 0xf0; + channel = buf[0] & 0x0f; + + switch(command) { + case 0x80: + fluid_synth_noteoff(synth, channel, buf[1]); + break; + case 0x90: + fluid_synth_noteon(synth, channel, buf[1], buf[2]); + break; + case 0xb0: + switch (buf[1]) { + case 0x06: + /* Data Entry Slider - course */ + if (rpn[channel] == 0) + fluid_synth_pitch_wheel_sens(synth, channel, buf[2]); + else + sciprintf("FluidSynth: RPN %i not supported\n", rpn[channel]); + case 0x64: + /* Registered Parameter Number (RPN) - fine */ + rpn[channel] &= ~0x7f; + rpn[channel] |= buf[2] & 0x7f; + break; + case 0x65: + /* Registered Parameter Number (RPN) - course */ + rpn[channel] &= ~0x3f80; + rpn[channel] |= (buf[2] & 0x7f) << 7; + break; + default: + fluid_synth_cc(synth, channel, buf[1], buf[2]); + } + break; + case 0xe0: + fluid_synth_pitch_bend(synth, channel, (buf[2] << 7) | buf[1]); + break; + default: + sciprintf("FluidSynth: MIDI command [%02x %02x %02x] not supported\n", buf[0], buf[1], buf[2]); + } + } + else + sciprintf("FluidSynth: Skipping invalid message of %i bytes.\n", len); + + return SFX_OK; +} + +static void +fluidsynth_midi_delay(struct _midi_writer *self, int ticks) +{ +} + +static void +fluidsynth_midi_reset_timer(struct _midi_writer *self) +{ +} + +static void +fluidsynth_midi_close(struct _midi_writer *self) +{ +} + +static midi_writer_t midi_writer_fluidsynth = { + "fluidsynth", + fluidsynth_midi_init, + fluidsynth_midi_set_option, + fluidsynth_midi_write, + fluidsynth_midi_delay, + NULL, + fluidsynth_midi_reset_timer, + fluidsynth_midi_close +}; + +/* Software sequencer */ + +static void +fluidsynth_poll(sfx_softseq_t *self, byte *dest, int count) +{ + fluid_synth_write_s16(synth, count, dest, 0, 2, dest + 2, 0, 2); +} + +static int +fluidsynth_init(sfx_softseq_t *self, byte *data_ptr, int data_length, + byte *data2_ptr, int data2_length) +{ + int sfont_id; + double min, max; + + if (0) { + sciprintf("FluidSynth ERROR: Mono sound output not supported.\n"); + return SFX_ERROR; + } + + gmseq = sfx_find_sequencer("General MIDI"); + if (!gmseq) { + sciprintf("FluidSynth ERROR: Unable to find General MIDI sequencer.\n"); + return SFX_ERROR; + } + + settings = new_fluid_settings(); + + fluid_settings_getnum_range(settings, "synth.sample-rate", &min, &max); + if (SAMPLE_RATE < min || SAMPLE_RATE > max) { + sciprintf("FluidSynth ERROR: Sample rate '%i' not supported. Valid " + "range is (%i-%i).\n", SAMPLE_RATE, (int) min, (int) max); + delete_fluid_settings(settings); + return SFX_ERROR; + } + + fluid_settings_setnum(settings, "synth.sample-rate", SAMPLE_RATE); + fluid_settings_setnum(settings, "synth.gain", 0.5f); + + synth = new_fluid_synth(settings); + + if ((sfont_id = fluid_synth_sfload(synth, soundfont, 1)) < 0) { + delete_fluid_synth(synth); + delete_fluid_settings(settings); + return SFX_ERROR; + } + + gmseq->open(data_length, data_ptr, data2_length, data2_ptr, + &midi_writer_fluidsynth); + + return SFX_OK; +} + +static void +fluidsynth_exit(sfx_softseq_t *self) +{ + delete_fluid_synth(synth); + delete_fluid_settings(settings); +} + +static void +fluidsynth_allstop(sfx_softseq_t *self) +{ + if (gmseq->allstop) + gmseq->allstop(); +} + +static void +fluidsynth_volume(sfx_softseq_t *self, int volume) +{ + if (gmseq->volume) + gmseq->volume(volume); +} + +static int +fluidsynth_set_option(sfx_softseq_t *self, char *name, char *value) +{ + return SFX_ERROR; +} + +static void +fluidsynth_event(sfx_softseq_t *self, byte cmd, int argc, byte *argv) +{ + gmseq->event(cmd, argc, argv); +} + +sfx_softseq_t sfx_softseq_fluidsynth = { + "fluidsynth", + "0.1", + fluidsynth_set_option, + fluidsynth_init, + fluidsynth_exit, + fluidsynth_volume, + fluidsynth_event, + fluidsynth_poll, + fluidsynth_allstop, + NULL, + 004, /* patch.004 */ + 001, /* patch.001 */ + 0x01, /* playflag */ + 1, /* do play channel 9 */ + 32, /* Max polypgony */ + {SAMPLE_RATE, CHANNELS, SFX_PCM_FORMAT_S16_NATIVE} +}; + +#endif /* HAVE_FLUIDSYNTH_H */ diff --git a/engines/sci/sfx/softseq/fmopl.c b/engines/sci/sfx/softseq/fmopl.c new file mode 100644 index 0000000000..6ea777c667 --- /dev/null +++ b/engines/sci/sfx/softseq/fmopl.c @@ -0,0 +1,1144 @@ +/*************************************************************************** + fmopl.c Copyright (C) 1999-2000 Tatsuyuki Satoh + 2001-2004 The ScummVM project + 2002 Solomon Peachy + 2004 Walter van Niftrik + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + LGPL licensed version of MAMEs fmopl (V0.37a modified) by + Tatsuyuki Satoh. Included from LGPL'ed AdPlug. + +***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#ifndef INLINE +# ifdef _MSC_VER +# define INLINE __inline +# else +# define INLINE inline +# endif +#endif + +#include +#include +#include +#include +#include + +#include "fmopl.h" + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/* -------------------- preliminary define section --------------------- */ +/* attack/decay rate time rate */ +#define OPL_ARRATE 141280 /* RATE 4 = 2826.24ms @ 3.6MHz */ +#define OPL_DRRATE 1956000 /* RATE 4 = 39280.64ms @ 3.6MHz */ + +#define FREQ_BITS 24 /* frequency turn */ + +/* counter bits = 20 , octerve 7 */ +#define FREQ_RATE (1<<(FREQ_BITS-20)) +#define TL_BITS (FREQ_BITS+2) + +/* final output shift , limit minimum and maximum */ +#define OPL_OUTSB (TL_BITS+3-16) /* OPL output final shift 16bit */ +#define OPL_MAXOUT (0x7fff< max ) + val = max; + else if ( val < min ) + val = min; + + return val; +} + +/* status set and IRQ handling */ +INLINE void OPL_STATUS_SET(FM_OPL *OPL, int flag) { + /* set status flag */ + OPL->status |= flag; + if(!(OPL->status & 0x80)) { + if(OPL->status & OPL->statusmask) { /* IRQ on */ + OPL->status |= 0x80; + /* callback user interrupt handler (IRQ is OFF to ON) */ + if(OPL->IRQHandler) + (OPL->IRQHandler)(OPL->IRQParam,1); + } + } +} + +/* status reset and IRQ handling */ +INLINE void OPL_STATUS_RESET(FM_OPL *OPL, int flag) { + /* reset status flag */ + OPL->status &= ~flag; + if((OPL->status & 0x80)) { + if (!(OPL->status & OPL->statusmask)) { + OPL->status &= 0x7f; + /* callback user interrupt handler (IRQ is ON to OFF) */ + if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0); + } + } +} + +/* IRQ mask set */ +INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL, int flag) { + OPL->statusmask = flag; + /* IRQ handling check */ + OPL_STATUS_SET(OPL,0); + OPL_STATUS_RESET(OPL,0); +} + +/* ----- key on ----- */ +INLINE void OPL_KEYON(OPL_SLOT *SLOT) { + /* sin wave restart */ + SLOT->Cnt = 0; + /* set attack */ + SLOT->evm = ENV_MOD_AR; + SLOT->evs = SLOT->evsa; + SLOT->evc = EG_AST; + SLOT->eve = EG_AED; +} +/* ----- key off ----- */ +INLINE void OPL_KEYOFF(OPL_SLOT *SLOT) { + if( SLOT->evm > ENV_MOD_RR) { + /* set envelope counter from envleope output */ + SLOT->evm = ENV_MOD_RR; + if( !(SLOT->evc & EG_DST) ) + //SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<evc = EG_DST; + SLOT->eve = EG_DED; + SLOT->evs = SLOT->evsr; + } +} + +/* ---------- calcrate Envelope Generator & Phase Generator ---------- */ +/* return : envelope output */ +INLINE guint32 OPL_CALC_SLOT(OPL_SLOT *SLOT) { + /* calcrate envelope generator */ + if((SLOT->evc += SLOT->evs) >= SLOT->eve) { + switch( SLOT->evm ){ + case ENV_MOD_AR: /* ATTACK -> DECAY1 */ + /* next DR */ + SLOT->evm = ENV_MOD_DR; + SLOT->evc = EG_DST; + SLOT->eve = SLOT->SL; + SLOT->evs = SLOT->evsd; + break; + case ENV_MOD_DR: /* DECAY -> SL or RR */ + SLOT->evc = SLOT->SL; + SLOT->eve = EG_DED; + if(SLOT->eg_typ) { + SLOT->evs = 0; + } else { + SLOT->evm = ENV_MOD_RR; + SLOT->evs = SLOT->evsr; + } + break; + case ENV_MOD_RR: /* RR -> OFF */ + SLOT->evc = EG_OFF; + SLOT->eve = EG_OFF + 1; + SLOT->evs = 0; + break; + } + } + /* calcrate envelope */ + return SLOT->TLL + ENV_CURVE[SLOT->evc>>ENV_BITS] + (SLOT->ams ? ams : 0); +} + +/* set algorythm connection */ +static void set_algorythm(OPL_CH *CH) { + int *carrier = &outd[0]; + CH->connect1 = CH->CON ? carrier : &feedback2; + CH->connect2 = carrier; +} + +/* ---------- frequency counter for operater update ---------- */ +INLINE void CALC_FCSLOT(OPL_CH *CH, OPL_SLOT *SLOT) { + int ksr; + + /* frequency step counter */ + SLOT->Incr = CH->fc * SLOT->mul; + ksr = CH->kcode >> SLOT->KSR; + + if( SLOT->ksr != ksr ) + { + SLOT->ksr = ksr; + /* attack , decay rate recalcration */ + SLOT->evsa = SLOT->AR[ksr]; + SLOT->evsd = SLOT->DR[ksr]; + SLOT->evsr = SLOT->RR[ksr]; + } + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); +} + +/* set multi,am,vib,EG-TYP,KSR,mul */ +INLINE void set_mul(FM_OPL *OPL, int slot, int v) { + OPL_CH *CH = &OPL->P_CH[slot / 2]; + OPL_SLOT *SLOT = &CH->SLOT[slot & 1]; + + SLOT->mul = MUL_TABLE[v & 0x0f]; + SLOT->KSR = (v & 0x10) ? 0 : 2; + SLOT->eg_typ = (v & 0x20) >> 5; + SLOT->vib = (v & 0x40); + SLOT->ams = (v & 0x80); + CALC_FCSLOT(CH, SLOT); +} + +/* set ksl & tl */ +INLINE void set_ksl_tl(FM_OPL *OPL, int slot, int v) { + OPL_CH *CH = &OPL->P_CH[slot / 2]; + OPL_SLOT *SLOT = &CH->SLOT[slot & 1]; + int ksl = v >> 6; /* 0 / 1.5 / 3 / 6 db/OCT */ + + SLOT->ksl = ksl ? 3-ksl : 31; + SLOT->TL = (int)((v & 0x3f) * (0.75 / EG_STEP)); /* 0.75db step */ + + if(!(OPL->mode & 0x80)) { /* not CSM latch total level */ + SLOT->TLL = SLOT->TL + (CH->ksl_base >> SLOT->ksl); + } +} + +/* set attack rate & decay rate */ +INLINE void set_ar_dr(FM_OPL *OPL, int slot, int v) { + OPL_CH *CH = &OPL->P_CH[slot / 2]; + OPL_SLOT *SLOT = &CH->SLOT[slot & 1]; + int ar = v >> 4; + int dr = v & 0x0f; + + SLOT->AR = ar ? &OPL->AR_TABLE[ar << 2] : RATE_0; + SLOT->evsa = SLOT->AR[SLOT->ksr]; + if(SLOT->evm == ENV_MOD_AR) + SLOT->evs = SLOT->evsa; + + SLOT->DR = dr ? &OPL->DR_TABLE[dr<<2] : RATE_0; + SLOT->evsd = SLOT->DR[SLOT->ksr]; + if(SLOT->evm == ENV_MOD_DR) + SLOT->evs = SLOT->evsd; +} + +/* set sustain level & release rate */ +INLINE void set_sl_rr(FM_OPL *OPL, int slot, int v) { + OPL_CH *CH = &OPL->P_CH[slot / 2]; + OPL_SLOT *SLOT = &CH->SLOT[slot & 1]; + int sl = v >> 4; + int rr = v & 0x0f; + + SLOT->SL = SL_TABLE[sl]; + if(SLOT->evm == ENV_MOD_DR) + SLOT->eve = SLOT->SL; + SLOT->RR = &OPL->DR_TABLE[rr<<2]; + SLOT->evsr = SLOT->RR[SLOT->ksr]; + if(SLOT->evm == ENV_MOD_RR) + SLOT->evs = SLOT->evsr; +} + +/* operator output calcrator */ +#define OP_OUT(slot,env,con) slot->wavetable[((slot->Cnt + con) / (0x1000000 / SIN_ENT)) & (SIN_ENT-1)][env] +/* ---------- calcrate one of channel ---------- */ +INLINE void OPL_CALC_CH(OPL_CH *CH) { + guint32 env_out; + OPL_SLOT *SLOT; + + feedback2 = 0; + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env_out=OPL_CALC_SLOT(SLOT); + if(env_out < (guint32)(EG_ENT - 1)) { + /* PG */ + if(SLOT->vib) + SLOT->Cnt += (SLOT->Incr * vib / VIB_RATE); + else + SLOT->Cnt += SLOT->Incr; + /* connectoion */ + if(CH->FB) { + int feedback1 = (CH->op1_out[0] + CH->op1_out[1]) >> CH->FB; + CH->op1_out[1] = CH->op1_out[0]; + *CH->connect1 += CH->op1_out[0] = OP_OUT(SLOT, env_out, feedback1); + } + else { + *CH->connect1 += OP_OUT(SLOT, env_out, 0); + } + }else { + CH->op1_out[1] = CH->op1_out[0]; + CH->op1_out[0] = 0; + } + /* SLOT 2 */ + SLOT = &CH->SLOT[SLOT2]; + env_out=OPL_CALC_SLOT(SLOT); + if(env_out < (guint32)(EG_ENT - 1)) { + /* PG */ + if(SLOT->vib) + SLOT->Cnt += (SLOT->Incr * vib / VIB_RATE); + else + SLOT->Cnt += SLOT->Incr; + /* connectoion */ + outd[0] += OP_OUT(SLOT, env_out, feedback2); + } +} + +/* ---------- calcrate rythm block ---------- */ +#define WHITE_NOISE_db 6.0 +INLINE void OPL_CALC_RH(OPL_CH *CH) { + guint32 env_tam, env_sd, env_top, env_hh; + int whitenoise = (int)((rand()&1) * (WHITE_NOISE_db / EG_STEP)); + int tone8; + + OPL_SLOT *SLOT; + int env_out; + + /* BD : same as FM serial mode and output level is large */ + feedback2 = 0; + /* SLOT 1 */ + SLOT = &CH[6].SLOT[SLOT1]; + env_out = OPL_CALC_SLOT(SLOT); + if(env_out < EG_ENT-1) { + /* PG */ + if(SLOT->vib) + SLOT->Cnt += (SLOT->Incr * vib / VIB_RATE); + else + SLOT->Cnt += SLOT->Incr; + /* connectoion */ + if(CH[6].FB) { + int feedback1 = (CH[6].op1_out[0] + CH[6].op1_out[1]) >> CH[6].FB; + CH[6].op1_out[1] = CH[6].op1_out[0]; + feedback2 = CH[6].op1_out[0] = OP_OUT(SLOT, env_out, feedback1); + } + else { + feedback2 = OP_OUT(SLOT, env_out, 0); + } + }else { + feedback2 = 0; + CH[6].op1_out[1] = CH[6].op1_out[0]; + CH[6].op1_out[0] = 0; + } + /* SLOT 2 */ + SLOT = &CH[6].SLOT[SLOT2]; + env_out = OPL_CALC_SLOT(SLOT); + if(env_out < EG_ENT-1) { + /* PG */ + if(SLOT->vib) + SLOT->Cnt += (SLOT->Incr * vib / VIB_RATE); + else + SLOT->Cnt += SLOT->Incr; + /* connectoion */ + outd[0] += OP_OUT(SLOT, env_out, feedback2) * 2; + } + + // SD (17) = mul14[fnum7] + white noise + // TAM (15) = mul15[fnum8] + // TOP (18) = fnum6(mul18[fnum8]+whitenoise) + // HH (14) = fnum7(mul18[fnum8]+whitenoise) + white noise + env_sd = OPL_CALC_SLOT(SLOT7_2) + whitenoise; + env_tam =OPL_CALC_SLOT(SLOT8_1); + env_top = OPL_CALC_SLOT(SLOT8_2); + env_hh = OPL_CALC_SLOT(SLOT7_1) + whitenoise; + + /* PG */ + if(SLOT7_1->vib) + SLOT7_1->Cnt += (2 * SLOT7_1->Incr * vib / VIB_RATE); + else + SLOT7_1->Cnt += 2 * SLOT7_1->Incr; + if(SLOT7_2->vib) + SLOT7_2->Cnt += ((CH[7].fc * 8) * vib / VIB_RATE); + else + SLOT7_2->Cnt += (CH[7].fc * 8); + if(SLOT8_1->vib) + SLOT8_1->Cnt += (SLOT8_1->Incr * vib / VIB_RATE); + else + SLOT8_1->Cnt += SLOT8_1->Incr; + if(SLOT8_2->vib) + SLOT8_2->Cnt += ((CH[8].fc * 48) * vib / VIB_RATE); + else + SLOT8_2->Cnt += (CH[8].fc * 48); + + tone8 = OP_OUT(SLOT8_2,whitenoise,0 ); + + /* SD */ + if(env_sd < (guint32)(EG_ENT - 1)) + outd[0] += OP_OUT(SLOT7_1, env_sd, 0) * 8; + /* TAM */ + if(env_tam < (guint32)(EG_ENT - 1)) + outd[0] += OP_OUT(SLOT8_1, env_tam, 0) * 2; + /* TOP-CY */ + if(env_top < (guint32)(EG_ENT - 1)) + outd[0] += OP_OUT(SLOT7_2, env_top, tone8) * 2; + /* HH */ + if(env_hh < (guint32)(EG_ENT-1)) + outd[0] += OP_OUT(SLOT7_2, env_hh, tone8) * 2; +} + +/* ----------- initialize time tabls ----------- */ +static void init_timetables(FM_OPL *OPL, int ARRATE, int DRRATE) { + int i; + double rate; + + /* make attack rate & decay rate tables */ + for (i = 0; i < 4; i++) + OPL->AR_TABLE[i] = OPL->DR_TABLE[i] = 0; + for (i = 4; i <= 60; i++){ + rate = OPL->freqbase; /* frequency rate */ + if(i < 60) + rate *= 1.0 + (i & 3) * 0.25; /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */ + rate *= 1 << ((i >> 2) - 1); /* b2-5 : shift bit */ + rate *= (double)(EG_ENT << ENV_BITS); + OPL->AR_TABLE[i] = (int)(rate / ARRATE); + OPL->DR_TABLE[i] = (int)(rate / DRRATE); + } + for (i = 60; i < 75; i++) { + OPL->AR_TABLE[i] = EG_AED-1; + OPL->DR_TABLE[i] = OPL->DR_TABLE[60]; + } +} + +/* ---------- generic table initialize ---------- */ +static int OPLOpenTable(void) { + int s,t; + double rate; + int i,j; + double pom; + + /* allocate dynamic tables */ + if((TL_TABLE = (int *)malloc(TL_MAX * 2 * sizeof(int))) == NULL) + return 0; + if((SIN_TABLE = (int **)malloc(SIN_ENT * 4 * sizeof(int *))) == NULL) { + free(TL_TABLE); + return 0; + } + if((AMS_TABLE = (int *)malloc(AMS_ENT * 2 * sizeof(int))) == NULL) { + free(TL_TABLE); + free(SIN_TABLE); + return 0; + } + if((VIB_TABLE = (int *)malloc(VIB_ENT * 2 * sizeof(int))) == NULL) { + free(TL_TABLE); + free(SIN_TABLE); + free(AMS_TABLE); + return 0; + } + /* make total level table */ + 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]; + } + /* fill volume off area */ + for (t = EG_ENT - 1; t < TL_MAX; t++){ + TL_TABLE[t] = TL_TABLE[TL_MAX + t] = 0; + } + + /* make sinwave table (total level offet) */ + /* degree 0 = degree 180 = off */ + SIN_TABLE[0] = SIN_TABLE[SIN_ENT /2 ] = &TL_TABLE[EG_ENT - 1]; + for (s = 1;s <= SIN_ENT / 4; s++){ + pom = sin(2 * PI * s / SIN_ENT); /* sin */ + pom = 20 * log10(1 / pom); /* decibel */ + j = (int)(pom / EG_STEP); /* TL_TABLE steps */ + + /* degree 0 - 90 , degree 180 - 90 : plus section */ + SIN_TABLE[ s] = SIN_TABLE[SIN_ENT / 2 - s] = &TL_TABLE[j]; + /* degree 180 - 270 , degree 360 - 270 : minus section */ + SIN_TABLE[SIN_ENT / 2 + s] = SIN_TABLE[SIN_ENT - s] = &TL_TABLE[TL_MAX + j]; + } + for (s = 0;s < SIN_ENT; s++) { + SIN_TABLE[SIN_ENT * 1 + s] = s < (SIN_ENT / 2) ? SIN_TABLE[s] : &TL_TABLE[EG_ENT]; + SIN_TABLE[SIN_ENT * 2 + s] = SIN_TABLE[s % (SIN_ENT / 2)]; + SIN_TABLE[SIN_ENT * 3 + s] = (s / (SIN_ENT / 4)) & 1 ? &TL_TABLE[EG_ENT] : SIN_TABLE[SIN_ENT * 2 + s]; + } + + /* envelope counter -> envelope output table */ + for (i=0; i < EG_ENT; i++) { + /* ATTACK curve */ + pom = pow(((double)(EG_ENT - 1 - i) / EG_ENT), 8) * EG_ENT; + /* if( pom >= EG_ENT ) pom = EG_ENT-1; */ + ENV_CURVE[i] = (int)pom; + /* DECAY ,RELEASE curve */ + ENV_CURVE[(EG_DST >> ENV_BITS) + i]= i; + } + /* off */ + ENV_CURVE[EG_OFF >> ENV_BITS]= EG_ENT - 1; + /* make LFO ams table */ + for (i=0; i < AMS_ENT; i++) { + pom = (1.0 + sin(2 * PI * i / AMS_ENT)) / 2; /* sin */ + AMS_TABLE[i] = (int)((1.0 / EG_STEP) * pom); /* 1dB */ + AMS_TABLE[AMS_ENT + i] = (int)((4.8 / EG_STEP) * pom); /* 4.8dB */ + } + /* make LFO vibrate table */ + for (i=0; i < VIB_ENT; i++) { + /* 100cent = 1seminote = 6% ?? */ + pom = (double)VIB_RATE * 0.06 * sin(2 * PI * i / VIB_ENT); /* +-100sect step */ + VIB_TABLE[i] = (int)(VIB_RATE + (pom * 0.07)); /* +- 7cent */ + VIB_TABLE[VIB_ENT + i] = (int)(VIB_RATE + (pom * 0.14)); /* +-14cent */ + } + return 1; +} + +static void OPLCloseTable(void) { + free(TL_TABLE); + free(SIN_TABLE); + free(AMS_TABLE); + free(VIB_TABLE); +} + +/* CSM Key Controll */ +INLINE void CSMKeyControll(OPL_CH *CH) { + OPL_SLOT *slot1 = &CH->SLOT[SLOT1]; + OPL_SLOT *slot2 = &CH->SLOT[SLOT2]; + /* all key off */ + OPL_KEYOFF(slot1); + OPL_KEYOFF(slot2); + /* total level latch */ + slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl); + slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl); + /* key on */ + CH->op1_out[0] = CH->op1_out[1] = 0; + OPL_KEYON(slot1); + OPL_KEYON(slot2); +} + +/* ---------- opl initialize ---------- */ +static void OPL_initalize(FM_OPL *OPL) { + int fn; + + /* frequency base */ + OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / OPL->rate) / 72 : 0; + /* Timer base time */ + OPL->TimerBase = 1.0/((double)OPL->clock / 72.0 ); + /* make time tables */ + init_timetables(OPL, OPL_ARRATE, OPL_DRRATE); + /* make fnumber -> increment counter table */ + for( fn=0; fn < 1024; fn++) { + OPL->FN_TABLE[fn] = (guint32)(OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2); + } + /* LFO freq.table */ + OPL->amsIncr = (int)(OPL->rate ? (double)AMS_ENT * (1 << AMS_SHIFT) / OPL->rate * 3.7 * ((double)OPL->clock/3600000) : 0); + OPL->vibIncr = (int)(OPL->rate ? (double)VIB_ENT * (1 << VIB_SHIFT) / OPL->rate * 6.4 * ((double)OPL->clock/3600000) : 0); +} + +/* ---------- write a OPL registers ---------- */ +void OPLWriteReg(FM_OPL *OPL, int r, int v) { + OPL_CH *CH; + int slot; + guint32 block_fnum; + + switch(r & 0xe0) { + case 0x00: /* 00-1f:controll */ + switch(r & 0x1f) { + case 0x01: + /* wave selector enable */ + if(OPL->type&OPL_TYPE_WAVESEL) { + OPL->wavesel = v & 0x20; + if(!OPL->wavesel) { + /* preset compatible mode */ + int c; + for(c=0; cmax_ch; c++) { + OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0]; + OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0]; + } + } + } + return; + case 0x02: /* Timer 1 */ + OPL->T[0] = (256-v) * 4; + break; + case 0x03: /* Timer 2 */ + OPL->T[1] = (256-v) * 16; + return; + case 0x04: /* IRQ clear / mask and Timer enable */ + if(v & 0x80) { /* IRQ flag clear */ + OPL_STATUS_RESET(OPL, 0x7f); + } + else { /* set IRQ mask ,timer enable*/ + guint8 st1 = v & 1; + guint8 st2 = (v >> 1) & 1; + /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */ + OPL_STATUS_RESET(OPL, v & 0x78); + OPL_STATUSMASK_SET(OPL,((~v) & 0x78) | 0x01); + /* timer 2 */ + if(OPL->st[1] != st2) { + double interval = st2 ? (double)OPL->T[1] * OPL->TimerBase : 0.0; + OPL->st[1] = st2; + if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam + 1, interval); + } + /* timer 1 */ + if(OPL->st[0] != st1) { + double interval = st1 ? (double)OPL->T[0] * OPL->TimerBase : 0.0; + OPL->st[0] = st1; + if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam + 0, interval); + } + } + return; + } + break; + case 0x20: /* am,vib,ksr,eg type,mul */ + slot = slot_array[r&0x1f]; + if(slot == -1) + return; + set_mul(OPL,slot,v); + return; + case 0x40: + slot = slot_array[r&0x1f]; + if(slot == -1) + return; + set_ksl_tl(OPL,slot,v); + return; + case 0x60: + slot = slot_array[r&0x1f]; + if(slot == -1) + return; + set_ar_dr(OPL,slot,v); + return; + case 0x80: + slot = slot_array[r&0x1f]; + if(slot == -1) + return; + set_sl_rr(OPL,slot,v); + return; + case 0xa0: + switch(r) { + case 0xbd: + /* amsep,vibdep,r,bd,sd,tom,tc,hh */ + { + guint8 rkey = OPL->rythm ^ v; + OPL->ams_table = &AMS_TABLE[v & 0x80 ? AMS_ENT : 0]; + OPL->vib_table = &VIB_TABLE[v & 0x40 ? VIB_ENT : 0]; + OPL->rythm = v & 0x3f; + if(OPL->rythm & 0x20) { + /* BD key on/off */ + if(rkey & 0x10) { + if(v & 0x10) { + OPL->P_CH[6].op1_out[0] = OPL->P_CH[6].op1_out[1] = 0; + OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT1]); + OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT2]); + } + else { + OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1]); + OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2]); + } + } + /* SD key on/off */ + if(rkey & 0x08) { + if(v & 0x08) + OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT2]); + else + OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2]); + }/* TAM key on/off */ + if(rkey & 0x04) { + if(v & 0x04) + OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT1]); + else + OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1]); + } + /* TOP-CY key on/off */ + if(rkey & 0x02) { + if(v & 0x02) + OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT2]); + else + OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2]); + } + /* HH key on/off */ + if(rkey & 0x01) { + if(v & 0x01) + OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT1]); + else + OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1]); + } + } + } + return; + } + /* keyon,block,fnum */ + if((r & 0x0f) >= ADLIB_VOICES) + return; + CH = &OPL->P_CH[r & 0x0f]; + if(!(r&0x10)) { /* a0-a8 */ + block_fnum = (CH->block_fnum & 0x1f00) | v; + } + else { /* b0-b8 */ + int keyon = (v >> 5) & 1; + block_fnum = ((v & 0x1f) << 8) | (CH->block_fnum & 0xff); + if(CH->keyon != keyon) { + if((CH->keyon=keyon)) { + CH->op1_out[0] = CH->op1_out[1] = 0; + OPL_KEYON(&CH->SLOT[SLOT1]); + OPL_KEYON(&CH->SLOT[SLOT2]); + } + else { + OPL_KEYOFF(&CH->SLOT[SLOT1]); + OPL_KEYOFF(&CH->SLOT[SLOT2]); + } + } + } + /* update */ + if(CH->block_fnum != block_fnum) { + int blockRv = 7 - (block_fnum >> 10); + int fnum = block_fnum & 0x3ff; + CH->block_fnum = block_fnum; + CH->ksl_base = KSL_TABLE[block_fnum >> 6]; + CH->fc = OPL->FN_TABLE[fnum] >> blockRv; + CH->kcode = CH->block_fnum >> 9; + if((OPL->mode & 0x40) && CH->block_fnum & 0x100) + CH->kcode |=1; + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + return; + case 0xc0: + /* FB,C */ + if((r & 0x0f) >= ADLIB_VOICES) + return; + CH = &OPL->P_CH[r&0x0f]; + { + int feedback = (v >> 1) & 7; + CH->FB = feedback ? (8 + 1) - feedback : 0; + CH->CON = v & 1; + set_algorythm(CH); + } + return; + case 0xe0: /* wave type */ + slot = slot_array[r & 0x1f]; + if(slot == -1) + return; + CH = &OPL->P_CH[slot / 2]; + if(OPL->wavesel) { + CH->SLOT[slot&1].wavetable = &SIN_TABLE[(v & 0x03) * SIN_ENT]; + } + return; + } +} + +/* lock/unlock for common table */ +static int OPL_LockTable(void) +{ + num_lock++; + if(num_lock>1) + return 0; + /* first time */ + cur_chip = NULL; + /* allocate total level table (128kb space) */ + if(!OPLOpenTable()) { + num_lock--; + return -1; + } + return 0; +} + +static void OPL_UnLockTable(void) { + if(num_lock) + num_lock--; + if(num_lock) + return; + /* last time */ + cur_chip = NULL; + OPLCloseTable(); +} + +/*******************************************************************************/ +/* YM3812 local section */ +/*******************************************************************************/ + +/* ---------- update one of chip ----------- */ +void YM3812UpdateOne(FM_OPL *OPL, gint16 *buffer, int length, int interleave) { + int i; + int data; + gint16 *buf = buffer; + guint32 amsCnt = OPL->amsCnt; + guint32 vibCnt = OPL->vibCnt; + guint8 rythm = OPL->rythm & 0x20; + OPL_CH *CH, *R_CH; + + if((void *)OPL != cur_chip){ + cur_chip = (void *)OPL; + /* channel pointers */ + S_CH = OPL->P_CH; + E_CH = &S_CH[9]; + /* rythm slot */ + SLOT7_1 = &S_CH[7].SLOT[SLOT1]; + SLOT7_2 = &S_CH[7].SLOT[SLOT2]; + SLOT8_1 = &S_CH[8].SLOT[SLOT1]; + SLOT8_2 = &S_CH[8].SLOT[SLOT2]; + /* LFO state */ + amsIncr = OPL->amsIncr; + vibIncr = OPL->vibIncr; + ams_table = OPL->ams_table; + vib_table = OPL->vib_table; + } + R_CH = rythm ? &S_CH[6] : E_CH; + for(i = 0; i < length; i++) { + /* channel A channel B channel C */ + /* LFO */ + ams = ams_table[(amsCnt += amsIncr) >> AMS_SHIFT]; + vib = vib_table[(vibCnt += vibIncr) >> VIB_SHIFT]; + outd[0] = 0; + /* FM part */ + for(CH=S_CH; CH < R_CH; CH++) + OPL_CALC_CH(CH); + /* Rythn part */ + if(rythm) + OPL_CALC_RH(S_CH); +#ifdef TWELVE_VOICE + for(CH=&S_CH[9]; CH < &S_CH[ADLIB_VOICES]; CH++) + OPL_CALC_CH(CH); +#endif + /* limit check */ + data = Limit(outd[0], OPL_MAXOUT, OPL_MINOUT); + /* store to sound buffer */ + buf[i << interleave] = data >> OPL_OUTSB; + } + + OPL->amsCnt = amsCnt; + OPL->vibCnt = vibCnt; +} + +/* ---------- reset a chip ---------- */ +void OPLResetChip(FM_OPL *OPL) { + int c,s; + int i; + + /* reset chip */ + OPL->mode = 0; /* normal mode */ + OPL_STATUS_RESET(OPL, 0x7f); + /* reset with register write */ + OPLWriteReg(OPL, 0x01,0); /* wabesel disable */ + OPLWriteReg(OPL, 0x02,0); /* Timer1 */ + OPLWriteReg(OPL, 0x03,0); /* Timer2 */ + OPLWriteReg(OPL, 0x04,0); /* IRQ mask clear */ + for(i = 0xff; i >= 0x20; i--) + OPLWriteReg(OPL,i,0); + /* reset OPerator parameter */ + 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++ ) { + /* wave table */ + CH->SLOT[s].wavetable = &SIN_TABLE[0]; + /* CH->SLOT[s].evm = ENV_MOD_RR; */ + CH->SLOT[s].evc = EG_OFF; + CH->SLOT[s].eve = EG_OFF + 1; + CH->SLOT[s].evs = 0; + } + } +} + +/* ---------- Create a virtual YM3812 ---------- */ +/* 'rate' is sampling rate and 'bufsiz' is the size of the */ +FM_OPL *OPLCreate(int type, int clock, int rate) { + char *ptr; + FM_OPL *OPL; + int state_size; +#ifdef TWELVE_VOICE + int max_ch = 12; +#else + int max_ch = 9; /* normaly 9 channels */ +#endif + + if( OPL_LockTable() == -1) + return NULL; + /* allocate OPL state space */ + state_size = sizeof(FM_OPL); + state_size += sizeof(OPL_CH) * max_ch; + + /* allocate memory block */ + ptr = (char *)calloc(state_size, 1); + if(ptr == NULL) + return NULL; + + /* clear */ + memset(ptr, 0, state_size); + OPL = (FM_OPL *)ptr; ptr += sizeof(FM_OPL); + OPL->P_CH = (OPL_CH *)ptr; ptr += sizeof(OPL_CH) * max_ch; + + /* set channel state pointer */ + OPL->type = type; + OPL->clock = clock; + OPL->rate = rate; + OPL->max_ch = max_ch; + + /* init grobal tables */ + OPL_initalize(OPL); + + /* reset chip */ + OPLResetChip(OPL); + return OPL; +} + +/* ---------- Destroy one of vietual YM3812 ---------- */ +void OPLDestroy(FM_OPL *OPL) { + OPL_UnLockTable(); + free(OPL); +} + +/* ---------- Option handlers ---------- */ + +void OPLSetTimerHandler(FM_OPL *OPL, OPL_TIMERHANDLER TimerHandler,int channelOffset) { + OPL->TimerHandler = TimerHandler; + OPL->TimerParam = channelOffset; +} + +void OPLSetIRQHandler(FM_OPL *OPL, OPL_IRQHANDLER IRQHandler, int param) { + OPL->IRQHandler = IRQHandler; + OPL->IRQParam = param; +} +void OPLSetUpdateHandler(FM_OPL *OPL, OPL_UPDATEHANDLER UpdateHandler,int param) { + OPL->UpdateHandler = UpdateHandler; + OPL->UpdateParam = param; +} + +/* ---------- YM3812 I/O interface ---------- */ +int OPLWrite(FM_OPL *OPL,int a,int v) { + if(!(a & 1)) { /* address port */ + OPL->address = v & 0xff; + } + else { /* data port */ + if(OPL->UpdateHandler) + OPL->UpdateHandler(OPL->UpdateParam,0); + OPLWriteReg(OPL, OPL->address,v); + } + return OPL->status >> 7; +} + +unsigned char OPLRead(FM_OPL *OPL,int a) { + if(!(a & 1)) { /* status port */ + return OPL->status & (OPL->statusmask | 0x80); + } + /* data port */ + switch(OPL->address) { + case 0x05: /* KeyBoard IN */ + fprintf(stderr, "OPL:read unmapped KEYBOARD port\n"); + return 0; + case 0x19: /* I/O DATA */ + fprintf(stderr, "OPL:read unmapped I/O port\n"); + return 0; + case 0x1a: /* PCM-DATA */ + return 0; + } + return 0; +} + +int OPLTimerOver(FM_OPL *OPL, int c) { + if(c) { /* Timer B */ + OPL_STATUS_SET(OPL, 0x20); + } + else { /* Timer A */ + OPL_STATUS_SET(OPL, 0x40); + /* CSM mode key,TL controll */ + if(OPL->mode & 0x80) { /* CSM mode total level latch and auto key on */ + int ch; + if(OPL->UpdateHandler) + OPL->UpdateHandler(OPL->UpdateParam,0); + for(ch = 0; ch < 9; ch++) + CSMKeyControll(&OPL->P_CH[ch]); + } + } + /* reload timer */ + if (OPL->TimerHandler) + (OPL->TimerHandler)(OPL->TimerParam + c, (double)OPL->T[c] * OPL->TimerBase); + return OPL->status >> 7; +} diff --git a/engines/sci/sfx/softseq/fmopl.h b/engines/sci/sfx/softseq/fmopl.h new file mode 100644 index 0000000000..5bb1cd80d7 --- /dev/null +++ b/engines/sci/sfx/softseq/fmopl.h @@ -0,0 +1,168 @@ +/*************************************************************************** + fmopl.h Copyright (C) 1999-2000 Tatsuyuki Satoh + 2001-2004 The ScummVM project + 2002 Solomon Peachy + 2004 Walter van Niftrik + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + LGPL licensed version of MAMEs fmopl (V0.37a modified) by + Tatsuyuki Satoh. Included from LGPL'ed AdPlug. + +***************************************************************************/ + +#ifndef FMOPL_H_ +#define FMOPL_H_ + +#include + +#define TWELVE_VOICE 1 +#ifdef TWELVE_VOICE +#define ADLIB_VOICES 12 +#else +#define ADLIB_VOICES 9 +#endif + +enum { + FMOPL_ENV_BITS_HQ = 16, + FMOPL_ENV_BITS_LQ = 8, + FMOPL_EG_ENT_HQ = 4096, + FMOPL_EG_ENT_LQ = 128 +}; + + +typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec); +typedef void (*OPL_IRQHANDLER)(int param,int irq); +typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us); + +#define OPL_TYPE_WAVESEL 0x01 /* waveform select */ + +/* Saving is necessary for member of the 'R' mark for suspend/resume */ +/* ---------- OPL one of slot ---------- */ +typedef struct fm_opl_slot { + int TL; /* total level :TL << 8 */ + int TLL; /* adjusted now TL */ + guint8 KSR; /* key scale rate :(shift down bit) */ + int *AR; /* attack rate :&AR_TABLE[AR<<2] */ + int *DR; /* decay rate :&DR_TABLE[DR<<2] */ + int SL; /* sustain level :SL_TABLE[SL] */ + int *RR; /* release rate :&DR_TABLE[RR<<2] */ + guint8 ksl; /* keyscale level :(shift down bits) */ + guint8 ksr; /* key scale rate :kcode>>KSR */ + guint32 mul; /* multiple :ML_TABLE[ML] */ + guint32 Cnt; /* frequency count */ + guint32 Incr; /* frequency step */ + + /* envelope generator state */ + guint8 eg_typ;/* envelope type flag */ + guint8 evm; /* envelope phase */ + int evc; /* envelope counter */ + int eve; /* envelope counter end point */ + int evs; /* envelope counter step */ + int evsa; /* envelope step for AR :AR[ksr] */ + int evsd; /* envelope step for DR :DR[ksr] */ + int evsr; /* envelope step for RR :RR[ksr] */ + + /* LFO */ + guint8 ams; /* ams flag */ + guint8 vib; /* vibrate flag */ + /* wave selector */ + int **wavetable; +} OPL_SLOT; + +/* ---------- OPL one of channel ---------- */ +typedef struct fm_opl_channel { + OPL_SLOT SLOT[2]; + guint8 CON; /* connection type */ + guint8 FB; /* feed back :(shift down bit)*/ + int *connect1; /* slot1 output pointer */ + int *connect2; /* slot2 output pointer */ + int op1_out[2]; /* slot1 output for selfeedback */ + + /* phase generator state */ + guint32 block_fnum; /* block+fnum */ + guint8 kcode; /* key code : KeyScaleCode */ + guint32 fc; /* Freq. Increment base */ + guint32 ksl_base; /* KeyScaleLevel Base step */ + guint8 keyon; /* key on/off flag */ +} OPL_CH; + +/* OPL state */ +typedef struct fm_opl_f { + guint8 type; /* chip type */ + int clock; /* master clock (Hz) */ + int rate; /* sampling rate (Hz) */ + double freqbase; /* frequency base */ + double TimerBase; /* Timer base time (==sampling time) */ + guint8 address; /* address register */ + guint8 status; /* status flag */ + guint8 statusmask; /* status mask */ + guint32 mode; /* Reg.08 : CSM , notesel,etc. */ + + /* Timer */ + int T[2]; /* timer counter */ + guint8 st[2]; /* timer enable */ + + /* FM channel slots */ + OPL_CH *P_CH; /* pointer of CH */ + int max_ch; /* maximum channel */ + + /* Rythm sention */ + guint8 rythm; /* Rythm mode , key flag */ + + /* time tables */ + int AR_TABLE[75]; /* atttack rate tables */ + int DR_TABLE[75]; /* decay rate tables */ + guint32 FN_TABLE[1024];/* fnumber -> increment counter */ + + /* LFO */ + int *ams_table; + int *vib_table; + int amsCnt; + int amsIncr; + int vibCnt; + int vibIncr; + + /* wave selector enable flag */ + guint8 wavesel; + + /* external event callback handler */ + OPL_TIMERHANDLER TimerHandler; /* TIMER handler */ + int TimerParam; /* TIMER parameter */ + OPL_IRQHANDLER IRQHandler; /* IRQ handler */ + int IRQParam; /* IRQ parameter */ + OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */ + int UpdateParam; /* stream update parameter */ +} FM_OPL; + +/* ---------- Generic interface section ---------- */ +#define OPL_TYPE_YM3526 (0) +#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL) + +void OPLBuildTables(int ENV_BITS_PARAM, int EG_ENT_PARAM); + +FM_OPL *OPLCreate(int type, int clock, int rate); +void OPLDestroy(FM_OPL *OPL); +void OPLSetTimerHandler(FM_OPL *OPL, OPL_TIMERHANDLER TimerHandler, int channelOffset); +void OPLSetIRQHandler(FM_OPL *OPL, OPL_IRQHANDLER IRQHandler, int param); +void OPLSetUpdateHandler(FM_OPL *OPL, OPL_UPDATEHANDLER UpdateHandler, int param); + +void OPLResetChip(FM_OPL *OPL); +int OPLWrite(FM_OPL *OPL, int a, int v); +unsigned char OPLRead(FM_OPL *OPL, int a); +int OPLTimerOver(FM_OPL *OPL, int c); +void OPLWriteReg(FM_OPL *OPL, int r, int v); +void YM3812UpdateOne(FM_OPL *OPL, gint16 *buffer, int length, int interleave); +#endif diff --git a/engines/sci/sfx/softseq/mt32.cpp b/engines/sci/sfx/softseq/mt32.cpp new file mode 100644 index 0000000000..6b37673c9e --- /dev/null +++ b/engines/sci/sfx/softseq/mt32.cpp @@ -0,0 +1,228 @@ +/*************************************************************************** + Copyright (C) 2008 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantability, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +extern "C" { +#include "../softseq.h" +#include "../sequencer.h" +#include "../device.h" +#include "resource.h" +#include "sci_memory.h" +} + +#include "mt32/mt32emu.h" + +/* FIXME */ +#define FREESCI_GAMEDIR ".freesci" + +#define SAMPLE_RATE 32000 +#define CHANNELS SFX_PCM_STEREO_LR +#define STEREO 1 + +static MT32Emu::Synth *synth; +static sfx_sequencer_t *mt32seq; +static int initializing; + +/* MIDI writer */ + +static int +mt32_midi_init(struct _midi_writer *self) +{ + return SFX_OK; +} + +static int +mt32_midi_set_option(struct _midi_writer *self, char *name, char *value) +{ + return SFX_ERROR; +} + +static int +mt32_midi_write(struct _midi_writer *self, unsigned char *buf, int len) +{ + if (buf[0] == 0xf0) + synth->playSysex(buf, len); + else if (len < 4) { + MT32Emu::Bit32u msg; + msg = buf[0]; + if (len > 0) + msg |= buf[1] << 8; + if (len > 1) + msg |= buf[2] << 16; + synth->playMsg(msg); + } + else + sciprintf("MT32EMU: Skipping non-sysex message of more than 3 bytes.\n"); + + return SFX_OK; +} + +static void +mt32_midi_delay(struct _midi_writer *self, int ticks) +{ +} + +static void +mt32_midi_reset_timer(struct _midi_writer *self) +{ +} + +static void +mt32_midi_close(struct _midi_writer *self) +{ +} + +static midi_writer_t midi_writer_mt32 = { + "mt32", + mt32_midi_init, + mt32_midi_set_option, + mt32_midi_write, + mt32_midi_delay, + NULL, + mt32_midi_reset_timer, + mt32_midi_close +}; + +/* Software sequencer */ + +static void printDebug(void *userData, const char *fmt, va_list list) +{ + if (initializing) { + vprintf(fmt, list); + printf("\n"); + } +} + +static void +mt32_poll(sfx_softseq_t *self, byte *dest, int count) +{ + synth->render((MT32Emu::Bit16s *) dest, count); +} + +static int +mt32_init(sfx_softseq_t *self, byte *data_ptr, int data_length, byte *data2_ptr, + int data2_length) +{ + MT32Emu::SynthProperties prop; + char *home = sci_get_homedir(); + char *romdir; + + if (!home) { + sciprintf("MT32EMU: Couldn't determine home directory.\n"); + return SFX_ERROR; + } + + romdir = (char *) sci_malloc(sizeof(home) + 2 * sizeof(G_DIR_SEPARATOR_S) + + sizeof(FREESCI_GAMEDIR) + 1); + + strcpy(romdir, home); + strcat(romdir, G_DIR_SEPARATOR_S); + strcat(romdir, FREESCI_GAMEDIR); + strcat(romdir, G_DIR_SEPARATOR_S); + + mt32seq = sfx_find_sequencer("MT32"); + if (!mt32seq) { + sciprintf("MT32EMU: Unable to find MT32 sequencer.\n"); + return SFX_ERROR; + } + + prop.sampleRate = SAMPLE_RATE; + prop.useReverb = true; + prop.useDefaultReverb = true; + prop.baseDir = romdir; + prop.userData = NULL; + prop.report = NULL; + prop.printDebug = printDebug; + prop.openFile = NULL; + prop.closeFile = NULL; + + initializing = 1; + synth = new MT32Emu::Synth(); + if (!synth->open(prop)) + return SFX_ERROR; + initializing = 0; + + mt32seq->open(data_length, data_ptr, data2_length, data2_ptr, + &midi_writer_mt32); + + free(romdir); + + return SFX_OK; +} + +static void +mt32_exit(sfx_softseq_t *self) +{ + synth->close(); + delete synth; + + mt32seq->close(); +} + +static void +mt32_allstop(sfx_softseq_t *self) +{ + if (mt32seq->allstop) + mt32seq->allstop(); +} + +static void +mt32_volume(sfx_softseq_t *self, int volume) +{ + if (mt32seq->volume) + mt32seq->volume(volume / 2); /* High volume causes clipping. */ +} + +static int +mt32_set_option(sfx_softseq_t *self, char *name, char *value) +{ + return SFX_ERROR; +} + +static void +mt32_event(sfx_softseq_t *self, byte cmd, int argc, byte *argv) +{ + mt32seq->event(cmd, argc, argv); +} + +sfx_softseq_t sfx_softseq_mt32 = { + "mt32emu", + "0.1", + mt32_set_option, + mt32_init, + mt32_exit, + mt32_volume, + mt32_event, + mt32_poll, + mt32_allstop, + NULL, + 001, /* patch.001 */ + SFX_SEQ_PATCHFILE_NONE, + 0x01, /* playflag */ + 1, /* do play channel 9 */ + 32, /* Max polypgony */ + {SAMPLE_RATE, CHANNELS, SFX_PCM_FORMAT_S16_NATIVE} +}; diff --git a/engines/sci/sfx/softseq/mt32/Makefile.am b/engines/sci/sfx/softseq/mt32/Makefile.am new file mode 100644 index 0000000000..5d2ffa31c8 --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/Makefile.am @@ -0,0 +1,5 @@ +noinst_HEADERS = freeverb.h mt32emu.h part.h partialManager.h synth.h \ +i386.h mt32_file.h partial.h structures.h tables.h +noinst_LIBRARIES = libmt32emu.a +libmt32emu_a_SOURCES = freeverb.cpp mt32_file.cpp partial.cpp synth.cpp \ +i386.cpp part.cpp partialManager.cpp tables.cpp diff --git a/engines/sci/sfx/softseq/mt32/freeverb.cpp b/engines/sci/sfx/softseq/mt32/freeverb.cpp new file mode 100644 index 0000000000..1c3aab0494 --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/freeverb.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$ + * + */ + +// Comb filter implementation +// +// Written by +// http://www.dreampoint.co.uk +// This code is public domain + +#include "freeverb.h" + +comb::comb() { + filterstore = 0; + bufidx = 0; +} + +void comb::setbuffer(float *buf, int size) { + buffer = buf; + bufsize = size; +} + +void comb::mute() { + for (int i = 0; i < bufsize; i++) + buffer[i] = 0; +} + +void comb::setdamp(float val) { + damp1 = val; + damp2 = 1 - val; +} + +float comb::getdamp() { + return damp1; +} + +void comb::setfeedback(float val) { + feedback = val; +} + +float comb::getfeedback() { + return feedback; +} + +// Allpass filter implementation + +allpass::allpass() { + bufidx = 0; +} + +void allpass::setbuffer(float *buf, int size) { + buffer = buf; + bufsize = size; +} + +void allpass::mute() { + for (int i = 0; i < bufsize; i++) + buffer[i] = 0; +} + +void allpass::setfeedback(float val) { + feedback = val; +} + +float allpass::getfeedback() { + return feedback; +} + +// Reverb model implementation + +revmodel::revmodel() { + // Tie the components to their buffers + combL[0].setbuffer(bufcombL1,combtuningL1); + combR[0].setbuffer(bufcombR1,combtuningR1); + combL[1].setbuffer(bufcombL2,combtuningL2); + combR[1].setbuffer(bufcombR2,combtuningR2); + combL[2].setbuffer(bufcombL3,combtuningL3); + combR[2].setbuffer(bufcombR3,combtuningR3); + combL[3].setbuffer(bufcombL4,combtuningL4); + combR[3].setbuffer(bufcombR4,combtuningR4); + combL[4].setbuffer(bufcombL5,combtuningL5); + combR[4].setbuffer(bufcombR5,combtuningR5); + combL[5].setbuffer(bufcombL6,combtuningL6); + combR[5].setbuffer(bufcombR6,combtuningR6); + combL[6].setbuffer(bufcombL7,combtuningL7); + combR[6].setbuffer(bufcombR7,combtuningR7); + combL[7].setbuffer(bufcombL8,combtuningL8); + combR[7].setbuffer(bufcombR8,combtuningR8); + allpassL[0].setbuffer(bufallpassL1,allpasstuningL1); + allpassR[0].setbuffer(bufallpassR1,allpasstuningR1); + allpassL[1].setbuffer(bufallpassL2,allpasstuningL2); + allpassR[1].setbuffer(bufallpassR2,allpasstuningR2); + allpassL[2].setbuffer(bufallpassL3,allpasstuningL3); + allpassR[2].setbuffer(bufallpassR3,allpasstuningR3); + allpassL[3].setbuffer(bufallpassL4,allpasstuningL4); + allpassR[3].setbuffer(bufallpassR4,allpasstuningR4); + + // Set default values + allpassL[0].setfeedback(0.5f); + allpassR[0].setfeedback(0.5f); + allpassL[1].setfeedback(0.5f); + allpassR[1].setfeedback(0.5f); + allpassL[2].setfeedback(0.5f); + allpassR[2].setfeedback(0.5f); + allpassL[3].setfeedback(0.5f); + allpassR[3].setfeedback(0.5f); + setwet(initialwet); + setroomsize(initialroom); + setdry(initialdry); + setdamp(initialdamp); + setwidth(initialwidth); + setmode(initialmode); + + // Buffer will be full of rubbish - so we MUST mute them + mute(); +} + +void revmodel::mute() { + int i; + + if (getmode() >= freezemode) + return; + + for (i = 0; i < numcombs; i++) { + combL[i].mute(); + combR[i].mute(); + } + + for (i = 0; i < numallpasses; i++) { + allpassL[i].mute(); + allpassR[i].mute(); + } +} + +void revmodel::processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip) { + float outL, outR, input; + + while (numsamples-- > 0) { + int i; + + outL = outR = 0; + input = (*inputL + *inputR) * gain; + + // Accumulate comb filters in parallel + for (i = 0; i < numcombs; i++) { + outL += combL[i].process(input); + outR += combR[i].process(input); + } + + // Feed through allpasses in series + for (i = 0; i < numallpasses; i++) { + outL = allpassL[i].process(outL); + outR = allpassR[i].process(outR); + } + + // Calculate output REPLACING anything already there + *outputL = outL * wet1 + outR * wet2 + *inputL * dry; + *outputR = outR * wet1 + outL * wet2 + *inputR * dry; + + // Increment sample pointers, allowing for interleave (if any) + inputL += skip; + inputR += skip; + outputL += skip; + outputR += skip; + } +} + +void revmodel::processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip) { + float outL, outR, input; + + while (numsamples-- > 0) { + int i; + + outL = outR = 0; + input = (*inputL + *inputR) * gain; + + // Accumulate comb filters in parallel + for (i = 0; i < numcombs; i++) { + outL += combL[i].process(input); + outR += combR[i].process(input); + } + + // Feed through allpasses in series + for (i = 0; i < numallpasses; i++) { + outL = allpassL[i].process(outL); + outR = allpassR[i].process(outR); + } + + // Calculate output MIXING with anything already there + *outputL += outL * wet1 + outR * wet2 + *inputL * dry; + *outputR += outR * wet1 + outL * wet2 + *inputR * dry; + + // Increment sample pointers, allowing for interleave (if any) + inputL += skip; + inputR += skip; + outputL += skip; + outputR += skip; + } +} + +void revmodel::update() { + // Recalculate internal values after parameter change + + int i; + + wet1 = wet * (width / 2 + 0.5f); + wet2 = wet * ((1 - width) / 2); + + if (mode >= freezemode) { + roomsize1 = 1; + damp1 = 0; + gain = muted; + } else { + roomsize1 = roomsize; + damp1 = damp; + gain = fixedgain; + } + + for (i = 0; i < numcombs; i++) { + combL[i].setfeedback(roomsize1); + combR[i].setfeedback(roomsize1); + } + + for (i = 0; i < numcombs; i++) { + combL[i].setdamp(damp1); + combR[i].setdamp(damp1); + } +} + +// The following get/set functions are not inlined, because +// speed is never an issue when calling them, and also +// because as you develop the reverb model, you may +// wish to take dynamic action when they are called. + +void revmodel::setroomsize(float value) { + roomsize = (value * scaleroom) + offsetroom; + update(); +} + +float revmodel::getroomsize() { + return (roomsize - offsetroom) / scaleroom; +} + +void revmodel::setdamp(float value) { + damp = value * scaledamp; + update(); +} + +float revmodel::getdamp() { + return damp / scaledamp; +} + +void revmodel::setwet(float value) { + wet = value * scalewet; + update(); +} + +float revmodel::getwet() { + return wet / scalewet; +} + +void revmodel::setdry(float value) { + dry = value * scaledry; +} + +float revmodel::getdry() { + return dry / scaledry; +} + +void revmodel::setwidth(float value) { + width = value; + update(); +} + +float revmodel::getwidth() { + return width; +} + +void revmodel::setmode(float value) { + mode = value; + update(); +} + +float revmodel::getmode() { + if (mode >= freezemode) + return 1; + else + return 0; +} diff --git a/engines/sci/sfx/softseq/mt32/freeverb.h b/engines/sci/sfx/softseq/mt32/freeverb.h new file mode 100644 index 0000000000..53c5307c5a --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/freeverb.h @@ -0,0 +1,239 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +// Macro for killing denormalled numbers +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// Based on IS_DENORMAL macro by Jon Watte +// This code is public domain + +#ifndef FREEVERB_H +#define FREEVERB_H + +#define undenormalise(sample) if (((*(unsigned int*)&sample) & 0x7f800000) == 0) sample = 0.0f + +// Comb filter class declaration + +class comb { +public: + comb(); + void setbuffer(float *buf, int size); + inline float process(float inp); + void mute(); + void setdamp(float val); + float getdamp(); + void setfeedback(float val); + float getfeedback(); +private: + float feedback; + float filterstore; + float damp1; + float damp2; + float *buffer; + int bufsize; + int bufidx; +}; + + +// Big to inline - but crucial for speed + +inline float comb::process(float input) { + float output; + + output = buffer[bufidx]; + undenormalise(output); + + filterstore = (output * damp2) + (filterstore * damp1); + undenormalise(filterstore); + + buffer[bufidx] = input + (filterstore * feedback); + + if (++bufidx >= bufsize) + bufidx = 0; + + return output; +} + +// Allpass filter declaration + +class allpass { +public: + allpass(); + void setbuffer(float *buf, int size); + inline float process(float inp); + void mute(); + void setfeedback(float val); + float getfeedback(); +private: + float feedback; + float *buffer; + int bufsize; + int bufidx; +}; + + +// Big to inline - but crucial for speed + +inline float allpass::process(float input) { + float output; + float bufout; + + bufout = buffer[bufidx]; + undenormalise(bufout); + + output = -input + bufout; + buffer[bufidx] = input + (bufout * feedback); + + if (++bufidx >= bufsize) + bufidx = 0; + + return output; +} + + +// Reverb model tuning values + +const int numcombs = 8; +const int numallpasses = 4; +const float muted = 0; +const float fixedgain = 0.015f; +const float scalewet = 3; +const float scaledry = 2; +const float scaledamp = 0.4f; +const float scaleroom = 0.28f; +const float offsetroom = 0.7f; +const float initialroom = 0.5f; +const float initialdamp = 0.5f; +const float initialwet = 1 / scalewet; +const float initialdry = 0; +const float initialwidth = 1; +const float initialmode = 0; +const float freezemode = 0.5f; +const int stereospread = 23; + +// These values assume 44.1KHz sample rate +// they will probably be OK for 48KHz sample rate +// but would need scaling for 96KHz (or other) sample rates. +// The values were obtained by listening tests. +const int combtuningL1 = 1116; +const int combtuningR1 = 1116 + stereospread; +const int combtuningL2 = 1188; +const int combtuningR2 = 1188 + stereospread; +const int combtuningL3 = 1277; +const int combtuningR3 = 1277 + stereospread; +const int combtuningL4 = 1356; +const int combtuningR4 = 1356 + stereospread; +const int combtuningL5 = 1422; +const int combtuningR5 = 1422 + stereospread; +const int combtuningL6 = 1491; +const int combtuningR6 = 1491 + stereospread; +const int combtuningL7 = 1557; +const int combtuningR7 = 1557 + stereospread; +const int combtuningL8 = 1617; +const int combtuningR8 = 1617 + stereospread; +const int allpasstuningL1 = 556; +const int allpasstuningR1 = 556 + stereospread; +const int allpasstuningL2 = 441; +const int allpasstuningR2 = 441 + stereospread; +const int allpasstuningL3 = 341; +const int allpasstuningR3 = 341 + stereospread; +const int allpasstuningL4 = 225; +const int allpasstuningR4 = 225 + stereospread; + + +// Reverb model declaration + +class revmodel { +public: + revmodel(); + void mute(); + void processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip); + void processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip); + void setroomsize(float value); + float getroomsize(); + void setdamp(float value); + float getdamp(); + void setwet(float value); + float getwet(); + void setdry(float value); + float getdry(); + void setwidth(float value); + float getwidth(); + void setmode(float value); + float getmode(); +private: + void update(); + + float gain; + float roomsize, roomsize1; + float damp, damp1; + float wet, wet1, wet2; + float dry; + float width; + float mode; + + // The following are all declared inline + // to remove the need for dynamic allocation + // with its subsequent error-checking messiness + + // Comb filters + comb combL[numcombs]; + comb combR[numcombs]; + + // Allpass filters + allpass allpassL[numallpasses]; + allpass allpassR[numallpasses]; + + // Buffers for the combs + float bufcombL1[combtuningL1]; + float bufcombR1[combtuningR1]; + float bufcombL2[combtuningL2]; + float bufcombR2[combtuningR2]; + float bufcombL3[combtuningL3]; + float bufcombR3[combtuningR3]; + float bufcombL4[combtuningL4]; + float bufcombR4[combtuningR4]; + float bufcombL5[combtuningL5]; + float bufcombR5[combtuningR5]; + float bufcombL6[combtuningL6]; + float bufcombR6[combtuningR6]; + float bufcombL7[combtuningL7]; + float bufcombR7[combtuningR7]; + float bufcombL8[combtuningL8]; + float bufcombR8[combtuningR8]; + + // Buffers for the allpasses + float bufallpassL1[allpasstuningL1]; + float bufallpassR1[allpasstuningR1]; + float bufallpassL2[allpasstuningL2]; + float bufallpassR2[allpasstuningR2]; + float bufallpassL3[allpasstuningL3]; + float bufallpassR3[allpasstuningR3]; + float bufallpassL4[allpasstuningL4]; + float bufallpassR4[allpasstuningR4]; +}; + +#endif diff --git a/engines/sci/sfx/softseq/mt32/i386.cpp b/engines/sci/sfx/softseq/mt32/i386.cpp new file mode 100644 index 0000000000..f092189d76 --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/i386.cpp @@ -0,0 +1,849 @@ +/* Copyright (c) 2003-2005 Various contributors + * + * 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. + */ + +#include "mt32emu.h" + +#ifdef MT32EMU_HAVE_X86 + +namespace MT32Emu { + +#ifndef _MSC_VER + +#define eflag(value) __asm__ __volatile__("pushfl \n popfl \n" : : "a"(value)) +#define cpuid_flag (1 << 21) + +static inline bool atti386_DetectCPUID() { + unsigned int result; + + // Is there a cpuid? + result = cpuid_flag; // set test + eflag(result); + if (!(result & cpuid_flag)) + return false; + + result = 0; // clear test + eflag(result); + if (result & cpuid_flag) + return false; + + return true; +} + +static inline bool atti386_DetectSIMD() { + unsigned int result; + + if (atti386_DetectCPUID() == false) + return false; + + /* check cpuid */ + __asm__ __volatile__( + "pushl %%ebx \n" \ + "movl $1, %%eax \n" \ + "cpuid \n" \ + "movl %%edx, %0 \n" \ + "popl %%ebx \n" \ + : "=r"(result) : : "eax", "ecx", "edx"); + + if (result & (1 << 25)) + return true; + + return false; +} + +static inline bool atti386_Detect3DNow() { + unsigned int result; + + if (atti386_DetectCPUID() == false) + return false; + + // get cpuid + __asm__ __volatile__( + "pushl %%ebx \n" \ + "movl $0x80000001, %%eax \n" \ + "cpuid \n" \ + "movl %%edx, %0 \n" \ + "popl %%ebx \n" \ + : "=r"(result) : : "eax", "ecx", "edx"); + + if (result & 0x80000000) + return true; + + return false; +} + + +static inline float atti386_iir_filter_sse(float *output, float *hist1_ptr, float *coef_ptr) { + __asm__ __volatile__ ( + "pushl %1 \n" \ + "pushl %2 \n" \ + "movss 0(%0), %%xmm1 \n" \ + "movups 0(%1), %%xmm2 \n" \ + "movlps 0(%2), %%xmm3 \n" \ + " \n" \ + "shufps $0x44, %%xmm3, %%xmm3 \n" \ + " \n" \ + "mulps %%xmm3, %%xmm2 \n" \ + " \n" \ + "subss %%xmm2, %%xmm1 \n" \ + "shufps $0x39, %%xmm2, %%xmm2 \n" \ + "subss %%xmm2, %%xmm1 \n" \ + " \n" \ + "movss %%xmm1, 0(%2) \n" \ + " \n" \ + "shufps $0x39, %%xmm2, %%xmm2 \n" \ + "addss %%xmm2, %%xmm1 \n" \ + " \n" \ + "shufps $0x39, %%xmm2, %%xmm2 \n" \ + "addss %%xmm2, %%xmm1 \n" \ + " \n" \ + "movss %%xmm3, 4(%2) \n" \ + " \n" \ + "addl $16, %1 \n" \ + "addl $8, %2 \n" \ + " \n" \ + "movups 0(%1), %%xmm2 \n" \ + " \n" \ + "movlps 0(%2), %%xmm3 \n" \ + "shufps $0x44, %%xmm3, %%xmm3 \n" \ + " \n" \ + "mulps %%xmm3, %%xmm2 \n" \ + " \n" \ + "subss %%xmm2, %%xmm1 \n" \ + "shufps $0x39, %%xmm2, %%xmm2 \n" \ + "subss %%xmm2, %%xmm1 \n" \ + " \n" \ + "movss %%xmm1, 0(%2) \n" \ + " \n" \ + "shufps $0x39, %%xmm2, %%xmm2 \n" \ + "addss %%xmm2, %%xmm1 \n" \ + " \n" \ + "shufps $0x39, %%xmm2, %%xmm2 \n" \ + "addss %%xmm2, %%xmm1 \n" \ + " \n" \ + "movss %%xmm3, 4(%2) \n" \ + "movss %%xmm1, 0(%0) \n" \ + "popl %2 \n" \ + "popl %1 \n" \ + : : "r"(output), "r"(coef_ptr), "r"(hist1_ptr) + : "memory" +#ifdef __SSE__ + , "xmm1", "xmm2", "xmm3" +#endif + ); + + return *output; +} + +static inline float atti386_iir_filter_3DNow(float output, float *hist1_ptr, float *coef_ptr) { + float tmp; + + __asm__ __volatile__ ( + "movq %0, %%mm1 \n" \ + " \n" \ + "movl %1, %%edi \n" \ + "movq 0(%%edi), %%mm2 \n" \ + " \n" \ + "movl %2, %%eax; \n" \ + "movq 0(%%eax), %%mm3 \n" \ + " \n" \ + "pfmul %%mm3, %%mm2 \n" \ + "pfsub %%mm2, %%mm1 \n" \ + " \n" \ + "psrlq $32, %%mm2 \n" \ + "pfsub %%mm2, %%mm1 \n" \ + " \n" \ + "movd %%mm1, %3 \n" \ + " \n" \ + "addl $8, %%edi \n" \ + "movq 0(%%edi), %%mm2 \n" \ + "movq 0(%%eax), %%mm3 \n" \ + " \n" \ + "pfmul %%mm3, %%mm2 \n" \ + "pfadd %%mm2, %%mm1 \n" \ + " \n" \ + "psrlq $32, %%mm2 \n" \ + "pfadd %%mm2, %%mm1 \n" \ + " \n" \ + "pushl %3 \n" \ + "popl 0(%%eax) \n" \ + " \n" \ + "movd %%mm3, 4(%%eax) \n" \ + " \n" \ + "addl $8, %%edi \n" \ + "addl $8, %%eax \n" \ + " \n" \ + "movq 0(%%edi), %%mm2 \n" \ + "movq 0(%%eax), %%mm3 \n" \ + " \n" \ + "pfmul %%mm3, %%mm2 \n" \ + "pfsub %%mm2, %%mm1 \n" \ + " \n" \ + "psrlq $32, %%mm2 \n" \ + "pfsub %%mm2, %%mm1 \n" \ + " \n" \ + "movd %%mm1, %3 \n" \ + " \n" \ + "addl $8, %%edi \n" \ + "movq 0(%%edi), %%mm2 \n" \ + "movq 0(%%eax), %%mm3 \n" \ + " \n" \ + "pfmul %%mm3, %%mm2 \n" \ + "pfadd %%mm2, %%mm1 \n" \ + " \n" \ + "psrlq $32, %%mm2 \n" \ + "pfadd %%mm2, %%mm1 \n" \ + " \n" \ + "pushl %3 \n" \ + "popl 0(%%eax) \n" \ + "movd %%mm3, 4(%%eax) \n" \ + " \n" \ + "movd %%mm1, %0 \n" \ + "femms \n" \ + : "=m"(output) : "g"(coef_ptr), "g"(hist1_ptr), "m"(tmp) + : "eax", "edi", "memory" +#ifdef __MMX__ + , "mm1", "mm2", "mm3" +#endif + ); + + return output; +} + +static inline void atti386_produceOutput1(int tmplen, Bit16s myvolume, Bit16s *useBuf, Bit16s *snd) { + __asm__ __volatile__( + "movl %0, %%ecx \n" \ + "movw %1, %%ax \n" \ + "shll $16, %%eax \n" \ + "movw %1, %%ax \n" \ + "movd %%eax, %%mm3 \n" \ + "movd %%eax, %%mm2 \n" \ + "psllq $32, %%mm3 \n" \ + "por %%mm2, %%mm3 \n" \ + "movl %2, %%esi \n" \ + "movl %3, %%edi \n" \ + "1: \n" \ + "movq 0(%%esi), %%mm1 \n" \ + "movq 0(%%edi), %%mm2 \n" \ + "pmulhw %%mm3, %%mm1 \n" \ + "paddw %%mm2, %%mm1 \n" \ + "movq %%mm1, 0(%%edi) \n" \ + " \n" \ + "addl $8, %%esi \n" \ + "addl $8, %%edi \n" \ + " \n" \ + "decl %%ecx \n" \ + "cmpl $0, %%ecx \n" \ + "jg 1b \n" \ + "emms \n" \ + : : "g"(tmplen), "g"(myvolume), "g"(useBuf), "g"(snd) + : "eax", "ecx", "edi", "esi", "memory" +#ifdef __MMX__ + , "mm1", "mm2", "mm3" +#endif + ); +} + +static inline void atti386_produceOutput2(Bit32u len, Bit16s *snd, float *sndbufl, float *sndbufr, float *multFactor) { + __asm__ __volatile__( + "movl %4, %%ecx \n" \ + "shrl $1, %%ecx \n" \ + "addl $4, %%ecx \n" \ + "pushl %%ecx \n" \ + " \n" \ + "movl %0, %%esi \n" \ + "movups 0(%%esi), %%xmm1 \n" \ + " \n" \ + "movl %1, %%esi \n" \ + "movl %2, %%edi \n" \ + "1: \n" \ + "xorl %%eax, %%eax \n" \ + "movw 0(%1), %%ax \n" \ + "cwde \n" \ + "incl %1 \n" \ + "incl %1 \n" \ + "movd %%eax, %%mm1 \n" \ + "psrlq $32, %%mm1 \n" \ + "movw 0(%1), %%ax \n" \ + "incl %1 \n" \ + "incl %1 \n" \ + "movd %%eax, %%mm2 \n" \ + "por %%mm2, %%mm1 \n" \ + " \n" \ + "decl %%ecx \n" \ + "jnz 1b \n" \ + " \n" \ + "popl %%ecx \n" \ + "movl %1, %%esi \n" \ + "movl %3, %%edi \n" \ + "incl %%esi \n" \ + "2: \n" \ + "decl %%ecx \n" \ + "jnz 2b \n" \ + : : "g"(multFactor), "r"(snd), "g"(sndbufl), "g"(sndbufr), "g"(len) + : "eax", "ecx", "edi", "esi", "mm1", "mm2", "xmm1", "memory"); +} + +static inline void atti386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) { + __asm__ __volatile__( + "movl %0, %%ecx \n" \ + "movl %1, %%esi \n" \ + "movl %2, %%edi \n" \ + "1: \n" \ + "movq 0(%%edi), %%mm1 \n" \ + "movq 0(%%esi), %%mm2 \n" \ + "paddw %%mm2, %%mm1 \n" \ + "movq %%mm1, 0(%%esi) \n" \ + "addl $8, %%edi \n" \ + "addl $8, %%esi \n" \ + "decl %%ecx \n" \ + "cmpl $0, %%ecx \n" \ + "jg 1b \n" \ + "emms \n" \ + : : "g"(len), "g"(buf1), "g"(buf2) + : "ecx", "edi", "esi", "memory" +#ifdef __MMX__ + , "mm1", "mm2" +#endif + ); +} + +static inline void atti386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) { + __asm__ __volatile__( + "movl %0, %%ecx \n" \ + "movl %1, %%esi \n" \ + "movl %2, %%edi \n" \ + "1: \n" \ + "movq 0(%%esi), %%mm1 \n" \ + "movq 0(%%edi), %%mm2 \n" \ + "movq %%mm1, %%mm3 \n" \ + "pmulhw %%mm2, %%mm1 \n" \ + "paddw %%mm3, %%mm1 \n" \ + "movq %%mm1, 0(%%esi) \n" \ + "addl $8, %%edi \n" \ + "addl $8, %%esi \n" \ + "decl %%ecx \n" \ + "cmpl $0, %%ecx \n" \ + "jg 1b \n" \ + "emms \n" \ + : : "g"(len), "g"(buf1), "g"(buf2) + : "ecx", "edi", "esi", "memory" +#ifdef __MMX__ + , "mm1", "mm2", "mm3" +#endif + ); +} + +static inline void atti386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) { + __asm__ __volatile__( + "movl %0, %%ecx \n" \ + "movl %1, %%esi \n" \ + "movl %2, %%edi \n" \ + "1: \n" \ + "movq 0(%%esi), %%mm1 \n" \ + "movq 0(%%edi), %%mm2 \n" \ + "pmulhw %%mm2, %%mm1 \n" \ + "movq %%mm1, 0(%%esi) \n" \ + "addl $8, %%edi \n" \ + "addl $8, %%esi \n" \ + "decl %%ecx \n" \ + "cmpl $0, %%ecx \n" \ + "jg 1b \n" \ + "emms \n" \ + : : "g"(len), "g"(buf1), "g"(buf2) + : "ecx", "edi", "esi", "memory" +#ifdef __MMX__ + , "mm1", "mm2" +#endif + ); +} + +static inline void atti386_partialProductOutput(int quadlen, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *p1buf) { + __asm__ __volatile__( + "movl %0, %%ecx \n" \ + "movw %1, %%ax \n" \ + "shll $16, %%eax \n" \ + "movw %2, %%ax \n" \ + "movd %%eax, %%mm1 \n" \ + "movd %%eax, %%mm2 \n" \ + "psllq $32, %%mm1 \n" \ + "por %%mm2, %%mm1 \n" \ + "movl %3, %%edi \n" \ + "movl %4, %%esi \n" \ + "pushl %%ebx \n" \ + "1: \n" \ + "movw 0(%%esi), %%bx \n" \ + "addl $2, %%esi \n" \ + "movw 0(%%esi), %%dx \n" \ + "addl $2, %%esi \n" \ + "" \ + "movw %%dx, %%ax \n" \ + "shll $16, %%eax \n" \ + "movw %%dx, %%ax \n" \ + "movd %%eax, %%mm2 \n" \ + "psllq $32, %%mm2 \n" \ + "movw %%bx, %%ax \n" \ + "shll $16, %%eax \n" \ + "movw %%bx, %%ax \n" \ + "movd %%eax, %%mm3 \n" \ + "por %%mm3, %%mm2 \n" \ + "" \ + "pmulhw %%mm1, %%mm2 \n" \ + "movq %%mm2, 0(%%edi) \n" \ + "addl $8, %%edi \n" \ + "" \ + "decl %%ecx \n" \ + "cmpl $0, %%ecx \n" \ + "jg 1b \n" \ + "emms \n" \ + "popl %%ebx \n" \ + : : "g"(quadlen), "g"(leftvol), "g"(rightvol), "g"(partialBuf), "g"(p1buf) + : "eax", "ecx", "edx", "edi", "esi", "memory" +#ifdef __MMX__ + , "mm1", "mm2", "mm3" +#endif + ); +} + +#endif + +bool DetectSIMD() { +#ifdef _MSC_VER + bool found_simd; + __asm { + pushfd + pop eax // get EFLAGS into eax + mov ebx,eax // keep a copy + xor eax,0x200000 + // toggle CPUID bit + + push eax + popfd // set new EFLAGS + pushfd + pop eax // EFLAGS back into eax + + xor eax,ebx + // have we changed the ID bit? + + je NO_SIMD + // No, no CPUID instruction + + // we could toggle the + // ID bit so CPUID is present + mov eax,1 + + cpuid // get processor features + test edx,1<<25 // check the SIMD bit + jz NO_SIMD + mov found_simd,1 + jmp DONE + NO_SIMD: + mov found_simd,0 + DONE: + } + return found_simd; +#else + return atti386_DetectSIMD(); +#endif +} + +bool Detect3DNow() { +#ifdef _MSC_VER + bool found3D = false; + __asm { + pushfd + pop eax + mov edx, eax + xor eax, 00200000h + push eax + popfd + pushfd + pop eax + xor eax, edx + jz NO_3DNOW + + mov eax, 80000000h + cpuid + + cmp eax, 80000000h + jbe NO_3DNOW + + mov eax, 80000001h + cpuid + test edx, 80000000h + jz NO_3DNOW + mov found3D, 1 +NO_3DNOW: + + } + return found3D; +#else + return atti386_Detect3DNow(); +#endif +} + +float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr) { + float output; + + // 1st number of coefficients array is overall input scale factor, or filter gain + output = input * (*coef_ptr++); + +#ifdef _MSC_VER + __asm { + + movss xmm1, output + + mov eax, coef_ptr + movups xmm2, [eax] + + mov eax, hist1_ptr + movlps xmm3, [eax] + shufps xmm3, xmm3, 44h + // hist1_ptr+1, hist1_ptr, hist1_ptr+1, hist1_ptr + + mulps xmm2, xmm3 + + subss xmm1, xmm2 + // Rotate elements right + shufps xmm2, xmm2, 39h + subss xmm1, xmm2 + + // Store new_hist + movss DWORD PTR [eax], xmm1 + + // Rotate elements right + shufps xmm2, xmm2, 39h + addss xmm1, xmm2 + + // Rotate elements right + shufps xmm2, xmm2, 39h + addss xmm1, xmm2 + + // Store previous hist + movss DWORD PTR [eax+4], xmm3 + + add coef_ptr, 16 + add hist1_ptr, 8 + + mov eax, coef_ptr + movups xmm2, [eax] + + mov eax, hist1_ptr + movlps xmm3, [eax] + shufps xmm3, xmm3, 44h + // hist1_ptr+1, hist1_ptr, hist1_ptr+1, hist1_ptr + + mulps xmm2, xmm3 + + subss xmm1, xmm2 + // Rotate elements right + shufps xmm2, xmm2, 39h + subss xmm1, xmm2 + + // Store new_hist + movss DWORD PTR [eax], xmm1 + + // Rotate elements right + shufps xmm2, xmm2, 39h + addss xmm1, xmm2 + + // Rotate elements right + shufps xmm2, xmm2, 39h + addss xmm1, xmm2 + + // Store previous hist + movss DWORD PTR [eax+4], xmm3 + + movss output, xmm1 + } +#else + output = atti386_iir_filter_sse(&output, hist1_ptr, coef_ptr); +#endif + return output; +} + +float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr) { + float output; + + // 1st number of coefficients array is overall input scale factor, or filter gain + output = input * (*coef_ptr++); + + // I find it very sad that 3DNow requires twice as many instructions as Intel's SSE + // Intel does have the upper hand here. +#ifdef _MSC_VER + float tmp; + __asm { + movq mm1, output + mov ebx, coef_ptr + movq mm2, [ebx] + + mov eax, hist1_ptr; + movq mm3, [eax] + + pfmul mm2, mm3 + pfsub mm1, mm2 + + psrlq mm2, 32 + pfsub mm1, mm2 + + // Store new hist + movd tmp, mm1 + + add ebx, 8 + movq mm2, [ebx] + movq mm3, [eax] + + pfmul mm2, mm3 + pfadd mm1, mm2 + + psrlq mm2, 32 + pfadd mm1, mm2 + + push tmp + pop DWORD PTR [eax] + + movd DWORD PTR [eax+4], mm3 + + add ebx, 8 + add eax, 8 + + movq mm2, [ebx] + movq mm3, [eax] + + pfmul mm2, mm3 + pfsub mm1, mm2 + + psrlq mm2, 32 + pfsub mm1, mm2 + + // Store new hist + movd tmp, mm1 + + add ebx, 8 + movq mm2, [ebx] + movq mm3, [eax] + + pfmul mm2, mm3 + pfadd mm1, mm2 + + psrlq mm2, 32 + pfadd mm1, mm2 + + push tmp + pop DWORD PTR [eax] + movd DWORD PTR [eax+4], mm3 + + movd output, mm1 + + femms + } +#else + output = atti386_iir_filter_3DNow(output, hist1_ptr, coef_ptr); +#endif + return output; +} + +#if MT32EMU_USE_MMX > 0 + +int i386_partialProductOutput(int len, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *mixedBuf) { + int tmplen = len >> 1; + if (tmplen == 0) { + return 0; + } +#ifdef _MSC_VER + __asm { + mov ecx,tmplen + mov ax, leftvol + shl eax,16 + mov ax, rightvol + movd mm1, eax + movd mm2, eax + psllq mm1, 32 + por mm1, mm2 + mov edi, partialBuf + mov esi, mixedBuf +mmxloop1: + mov bx, [esi] + add esi,2 + mov dx, [esi] + add esi,2 + + mov ax, dx + shl eax, 16 + mov ax, dx + movd mm2,eax + psllq mm2, 32 + mov ax, bx + shl eax, 16 + mov ax, bx + movd mm3,eax + por mm2,mm3 + + pmulhw mm2, mm1 + movq [edi], mm2 + add edi, 8 + + dec ecx + cmp ecx,0 + jg mmxloop1 + emms + } +#else + atti386_partialProductOutput(tmplen, leftvol, rightvol, partialBuf, mixedBuf); +#endif + return tmplen << 1; +} + +int i386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) { + int tmplen = len >> 2; + if (tmplen == 0) { + return 0; + } +#ifdef _MSC_VER + __asm { + mov ecx, tmplen + mov esi, buf1 + mov edi, buf2 + +mixloop1: + movq mm1, [edi] + movq mm2, [esi] + paddw mm1,mm2 + movq [esi],mm1 + add edi,8 + add esi,8 + + dec ecx + cmp ecx,0 + jg mixloop1 + emms + } +#else + atti386_mixBuffers(buf1, buf2, tmplen); +#endif + return tmplen << 2; +} + + +int i386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) { + int tmplen = len >> 2; + if (tmplen == 0) { + return 0; + } +#ifdef _MSC_VER + __asm { + mov ecx, tmplen + mov esi, buf1 + mov edi, buf2 + +mixloop2: + movq mm1, [esi] + movq mm2, [edi] + movq mm3, mm1 + pmulhw mm1, mm2 + paddw mm1,mm3 + movq [esi],mm1 + add edi,8 + add esi,8 + + dec ecx + cmp ecx,0 + jg mixloop2 + emms + } +#else + atti386_mixBuffersRingMix(buf1, buf2, tmplen); +#endif + return tmplen << 2; +} + +int i386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) { + int tmplen = len >> 2; + if (tmplen == 0) { + return 0; + } +#ifdef _MSC_VER + __asm { + mov ecx, tmplen + mov esi, buf1 + mov edi, buf2 + +mixloop3: + movq mm1, [esi] + movq mm2, [edi] + pmulhw mm1, mm2 + movq [esi],mm1 + add edi,8 + add esi,8 + + dec ecx + cmp ecx,0 + jg mixloop3 + emms + } +#else + atti386_mixBuffersRing(buf1, buf2, tmplen); +#endif + return tmplen << 2; +} + +int i386_produceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume) { + int tmplen = (len >> 1); + if (tmplen == 0) { + return 0; + } +#ifdef _MSC_VER + __asm { + mov ecx, tmplen + mov ax,volume + shl eax,16 + mov ax,volume + movd mm3,eax + movd mm2,eax + psllq mm3, 32 + por mm3,mm2 + mov esi, useBuf + mov edi, stream +mixloop4: + movq mm1, [esi] + movq mm2, [edi] + pmulhw mm1, mm3 + paddw mm1,mm2 + movq [edi], mm1 + + add esi,8 + add edi,8 + + dec ecx + cmp ecx,0 + jg mixloop4 + emms + } +#else + atti386_produceOutput1(tmplen, volume, useBuf, stream); +#endif + return tmplen << 1; +} + +#endif + +} + +#endif diff --git a/engines/sci/sfx/softseq/mt32/i386.h b/engines/sci/sfx/softseq/mt32/i386.h new file mode 100644 index 0000000000..e8644411cd --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/i386.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2003-2005 Various contributors + * + * 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. + */ + +#ifndef MT32EMU_I386_H +#define MT32EMU_I386_H + +namespace MT32Emu { +#ifdef MT32EMU_HAVE_X86 + +// Function that detects the availablity of SSE SIMD instructions +bool DetectSIMD(); +// Function that detects the availablity of 3DNow instructions +bool Detect3DNow(); + +float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr); +float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr); +float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr); + +#if MT32EMU_USE_MMX > 0 +int i386_partialProductOutput(int len, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *mixedBuf); +int i386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len); +int i386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len); +int i386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len); +int i386_produceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume); +#endif + +#endif + +} + +#endif diff --git a/engines/sci/sfx/softseq/mt32/mt32_file.cpp b/engines/sci/sfx/softseq/mt32/mt32_file.cpp new file mode 100644 index 0000000000..86cb29fd49 --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/mt32_file.cpp @@ -0,0 +1,112 @@ +/* Copyright (c) 2003-2005 Various contributors + * + * 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. + */ + +#include + +#include "mt32emu.h" + +namespace MT32Emu { + + bool ANSIFile::open(const char *filename, OpenMode mode) { + const char *fmode; + if (mode == OpenMode_read) { + fmode = "rb"; + } else { + fmode = "wb"; + } + fp = fopen(filename, fmode); + return (fp != NULL); + } + + void ANSIFile::close() { + fclose(fp); + } + + size_t ANSIFile::read(void *in, size_t size) { + return fread(in, 1, size, fp); + } + + bool ANSIFile::readLine(char *in, size_t size) { + return fgets(in, (int)size, fp) != NULL; + } + + bool ANSIFile::readBit8u(Bit8u *in) { + int c = fgetc(fp); + if (c == EOF) + return false; + *in = (Bit8u)c; + return true; + } + + bool File::readBit16u(Bit16u *in) { + Bit8u b[2]; + if (read(&b[0], 2) != 2) + return false; + *in = ((b[0] << 8) | b[1]); + return true; + } + + bool File::readBit32u(Bit32u *in) { + Bit8u b[4]; + if (read(&b[0], 4) != 4) + return false; + *in = ((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]); + return true; + } + + size_t ANSIFile::write(const void *out, size_t size) { + return fwrite(out, 1, size, fp); + } + + bool ANSIFile::writeBit8u(Bit8u out) { + return fputc(out, fp) != EOF; + } + + bool File::writeBit16u(Bit16u out) { + if (!writeBit8u((Bit8u)((out & 0xFF00) >> 8))) { + return false; + } + if (!writeBit8u((Bit8u)(out & 0x00FF))) { + return false; + } + return true; + } + + bool File::writeBit32u(Bit32u out) { + if (!writeBit8u((Bit8u)((out & 0xFF000000) >> 24))) { + return false; + } + if (!writeBit8u((Bit8u)((out & 0x00FF0000) >> 16))) { + return false; + } + if (!writeBit8u((Bit8u)((out & 0x0000FF00) >> 8))) { + return false; + } + if (!writeBit8u((Bit8u)(out & 0x000000FF))) { + return false; + } + return true; + } + + bool ANSIFile::isEOF() { + return feof(fp) != 0; + } +} diff --git a/engines/sci/sfx/softseq/mt32/mt32_file.h b/engines/sci/sfx/softseq/mt32/mt32_file.h new file mode 100644 index 0000000000..5f05c9e9ae --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/mt32_file.h @@ -0,0 +1,67 @@ +/* Copyright (c) 2003-2005 Various contributors + * + * 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. + */ + +#ifndef MT32EMU_FILE_H +#define MT32EMU_FILE_H + +#include + +namespace MT32Emu { + +class File { +public: + enum OpenMode { + OpenMode_read = 0, + OpenMode_write = 1 + }; + virtual ~File() {} + virtual void close() = 0; + virtual size_t read(void *in, size_t size) = 0; + virtual bool readLine(char *in, size_t size) = 0; + virtual bool readBit8u(Bit8u *in) = 0; + virtual bool readBit16u(Bit16u *in); + virtual bool readBit32u(Bit32u *in); + virtual size_t write(const void *out, size_t size) = 0; + virtual bool writeBit8u(Bit8u out) = 0; + // Note: May write a single byte to the file before failing + virtual bool writeBit16u(Bit16u out); + // Note: May write some (<4) bytes to the file before failing + virtual bool writeBit32u(Bit32u out); + virtual bool isEOF() = 0; +}; + +class ANSIFile: public File { +private: + FILE *fp; +public: + bool open(const char *filename, OpenMode mode); + void close(); + size_t read(void *in, size_t size); + bool readLine(char *in, size_t size); + bool readBit8u(Bit8u *in); + size_t write(const void *out, size_t size); + bool writeBit8u(Bit8u out); + bool isEOF(); +}; + +} + +#endif diff --git a/engines/sci/sfx/softseq/mt32/mt32emu.h b/engines/sci/sfx/softseq/mt32/mt32emu.h new file mode 100644 index 0000000000..0aa4df7488 --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/mt32emu.h @@ -0,0 +1,70 @@ +/* Copyright (c) 2003-2005 Various contributors + * + * 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. + */ + +#ifndef MT32EMU_MT32EMU_H +#define MT32EMU_MT32EMU_H + +// Debugging +// Show the instruments played +#define MT32EMU_MONITOR_INSTRUMENTS 1 +// Shows number of partials MT-32 is playing, and on which parts +#define MT32EMU_MONITOR_PARTIALS 0 +// Determines how the waveform cache file is handled (must be regenerated after sampling rate change) +#define MT32EMU_WAVECACHEMODE 0 // Load existing cache if possible, otherwise generate and save cache +//#define MT32EMU_WAVECACHEMODE 1 // Load existing cache if possible, otherwise generage but don't save cache +//#define MT32EMU_WAVECACHEMODE 2 // Ignore existing cache, generate and save cache +//#define MT32EMU_WAVECACHEMODE 3 // Ignore existing cache, generate but don't save cache + +// Configuration +// The maximum number of partials playing simultaneously +#define MT32EMU_MAX_PARTIALS 32 +// The maximum number of notes playing simultaneously per part. +// No point making it more than MT32EMU_MAX_PARTIALS, since each note needs at least one partial. +#define MT32EMU_MAX_POLY 32 +// This calculates the exact frequencies of notes as they are played, instead of offsetting from pre-cached semitones. Potentially very slow. +#define MT32EMU_ACCURATENOTES 0 + +#if (defined (_MSC_VER) && defined(_M_IX86)) +#define MT32EMU_HAVE_X86 +#elif defined(__GNUC__) +#if __GNUC__ >= 3 && defined(__i386__) +#define MT32EMU_HAVE_X86 +#endif +#endif + +#ifdef MT32EMU_HAVE_X86 +#define MT32EMU_USE_MMX 1 +#else +#define MT32EMU_USE_MMX 0 +#endif + +#include "freeverb.h" + +#include "structures.h" +#include "i386.h" +#include "mt32_file.h" +#include "tables.h" +#include "partial.h" +#include "partialManager.h" +#include "part.h" +#include "synth.h" + +#endif diff --git a/engines/sci/sfx/softseq/mt32/part.cpp b/engines/sci/sfx/softseq/mt32/part.cpp new file mode 100644 index 0000000000..b3d71bccca --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/part.cpp @@ -0,0 +1,632 @@ +/* Copyright (c) 2003-2005 Various contributors + * + * 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. + */ + +#include +#include + +#include "mt32emu.h" + +namespace MT32Emu { + +static const Bit8u PartialStruct[13] = { + 0, 0, 2, 2, 1, 3, + 3, 0, 3, 0, 2, 1, 3 }; + +static const Bit8u PartialMixStruct[13] = { + 0, 1, 0, 1, 1, 0, + 1, 3, 3, 2, 2, 2, 2 }; + +static const float floatKeyfollow[17] = { + -1.0f, -1.0f/2.0f, -1.0f/4.0f, 0.0f, + 1.0f/8.0f, 1.0f/4.0f, 3.0f/8.0f, 1.0f/2.0f, 5.0f/8.0f, 3.0f/4.0f, 7.0f/8.0f, 1.0f, + 5.0f/4.0f, 3.0f/2.0f, 2.0f, + 1.0009765625f, 1.0048828125f +}; + +//FIXME:KG: Put this dpoly stuff somewhere better +bool dpoly::isActive() const { + return partials[0] != NULL || partials[1] != NULL || partials[2] != NULL || partials[3] != NULL; +} + +Bit32u dpoly::getAge() const { + for (int i = 0; i < 4; i++) { + if (partials[i] != NULL) { + return partials[i]->age; + } + } + return 0; +} + +RhythmPart::RhythmPart(Synth *useSynth, unsigned int usePartNum): Part(useSynth, usePartNum) { + strcpy(name, "Rhythm"); + rhythmTemp = &synth->mt32ram.rhythmSettings[0]; + refresh(); +} + +Part::Part(Synth *useSynth, unsigned int usePartNum) { + this->synth = useSynth; + this->partNum = usePartNum; + patchCache[0].dirty = true; + holdpedal = false; + patchTemp = &synth->mt32ram.patchSettings[partNum]; + if (usePartNum == 8) { + // Nasty hack for rhythm + timbreTemp = NULL; + } else { + sprintf(name, "Part %d", partNum + 1); + timbreTemp = &synth->mt32ram.timbreSettings[partNum]; + } + currentInstr[0] = 0; + currentInstr[10] = 0; + expression = 127; + volumeMult = 0; + volumesetting.leftvol = 32767; + volumesetting.rightvol = 32767; + bend = 0.0f; + memset(polyTable,0,sizeof(polyTable)); + memset(patchCache, 0, sizeof(patchCache)); +} + +void Part::setHoldPedal(bool pedalval) { + if (holdpedal && !pedalval) { + holdpedal = false; + stopPedalHold(); + } else { + holdpedal = pedalval; + } +} + +void RhythmPart::setBend(unsigned int midiBend) { + synth->printDebug("%s: Setting bend (%d) not supported on rhythm", name, midiBend); + return; +} + +void Part::setBend(unsigned int midiBend) { + // FIXME:KG: Slightly unbalanced increments, but I wanted min -1.0, centre 0.0 and max 1.0 + if (midiBend <= 0x2000) { + bend = ((signed int)midiBend - 0x2000) / (float)0x2000; + } else { + bend = ((signed int)midiBend - 0x2000) / (float)0x1FFF; + } + // Loop through all partials to update their bend + for (int i = 0; i < MT32EMU_MAX_POLY; i++) { + for (int j = 0; j < 4; j++) { + if (polyTable[i].partials[j] != NULL) { + polyTable[i].partials[j]->setBend(bend); + } + } + } +} + +void RhythmPart::setModulation(unsigned int midiModulation) { + synth->printDebug("%s: Setting modulation (%d) not supported on rhythm", name, midiModulation); +} + +void Part::setModulation(unsigned int midiModulation) { + // Just a bloody guess, as always, before I get things figured out + for (int t = 0; t < 4; t++) { + if (patchCache[t].playPartial) { + int newrate = (patchCache[t].modsense * midiModulation) >> 7; + //patchCache[t].lfoperiod = lfotable[newrate]; + patchCache[t].lfodepth = newrate; + //FIXME:KG: timbreTemp->partial[t].lfo.depth = + } + } +} + +void RhythmPart::refresh() { + updateVolume(); + // (Re-)cache all the mapped timbres ahead of time + for (unsigned int drumNum = 0; drumNum < synth->controlROMMap->rhythmSettingsCount; drumNum++) { + int drumTimbreNum = rhythmTemp[drumNum].timbre; + if (drumTimbreNum >= 127) // 94 on MT-32 + continue; + Bit16s pan = rhythmTemp[drumNum].panpot; // They use R-L 0-14... + // FIXME:KG: Panning cache should be backed up to partials using it, too + if (pan < 7) { + drumPan[drumNum].leftvol = pan * 4681; + drumPan[drumNum].rightvol = 32767; + } else { + drumPan[drumNum].rightvol = (14 - pan) * 4681; + drumPan[drumNum].leftvol = 32767; + } + PatchCache *cache = drumCache[drumNum]; + backupCacheToPartials(cache); + for (int t = 0; t < 4; t++) { + // Common parameters, stored redundantly + cache[t].dirty = true; + cache[t].pitchShift = 0.0f; + cache[t].benderRange = 0.0f; + cache[t].pansetptr = &drumPan[drumNum]; + cache[t].reverb = rhythmTemp[drumNum].reverbSwitch > 0; + } + } +} + +void Part::refresh() { + updateVolume(); + backupCacheToPartials(patchCache); + for (int t = 0; t < 4; t++) { + // Common parameters, stored redundantly + patchCache[t].dirty = true; + patchCache[t].pitchShift = (patchTemp->patch.keyShift - 24) + (patchTemp->patch.fineTune - 50) / 100.0f; + patchCache[t].benderRange = patchTemp->patch.benderRange; + patchCache[t].pansetptr = &volumesetting; + patchCache[t].reverb = patchTemp->patch.reverbSwitch > 0; + } + memcpy(currentInstr, timbreTemp->common.name, 10); +} + +const char *Part::getCurrentInstr() const { + return ¤tInstr[0]; +} + +void RhythmPart::refreshTimbre(unsigned int absTimbreNum) { + for (int m = 0; m < 85; m++) { + if (rhythmTemp[m].timbre == absTimbreNum - 128) + drumCache[m][0].dirty = true; + } +} + +void Part::refreshTimbre(unsigned int absTimbreNum) { + if (getAbsTimbreNum() == absTimbreNum) { + memcpy(currentInstr, timbreTemp->common.name, 10); + patchCache[0].dirty = true; + } +} + +int Part::fixBiaslevel(int srcpnt, int *dir) { + int noteat = srcpnt & 0x3F; + int outnote; + if (srcpnt < 64) + *dir = 0; + else + *dir = 1; + outnote = 33 + noteat; + //synth->printDebug("Bias note %d, dir %d", outnote, *dir); + + return outnote; +} + +int Part::fixKeyfollow(int srckey) { + if (srckey>=0 && srckey<=16) { + int keyfix[17] = { -256*16, -128*16, -64*16, 0, 32*16, 64*16, 96*16, 128*16, (128+32)*16, 192*16, (192+32)*16, 256*16, (256+64)*16, (256+128)*16, (512)*16, 4100, 4116}; + return keyfix[srckey]; + } else { + //LOG(LOG_ERROR|LOG_MISC,"Missed key: %d", srckey); + return 256; + } +} + +void Part::abortPoly(dpoly *poly) { + if (!poly->isPlaying) { + return; + } + for (int i = 0; i < 4; i++) { + Partial *partial = poly->partials[i]; + if (partial != NULL) { + partial->deactivate(); + } + } + poly->isPlaying = false; +} + +void Part::setPatch(const PatchParam *patch) { + patchTemp->patch = *patch; +} + +void RhythmPart::setTimbre(TimbreParam * /*timbre*/) { + synth->printDebug("%s: Attempted to call setTimbre() - doesn't make sense for rhythm", name); +} + +void Part::setTimbre(TimbreParam *timbre) { + *timbreTemp = *timbre; +} + +unsigned int RhythmPart::getAbsTimbreNum() const { + synth->printDebug("%s: Attempted to call getAbsTimbreNum() - doesn't make sense for rhythm", name); + return 0; +} + +unsigned int Part::getAbsTimbreNum() const { + return (patchTemp->patch.timbreGroup * 64) + patchTemp->patch.timbreNum; +} + +void RhythmPart::setProgram(unsigned int patchNum) { + synth->printDebug("%s: Attempt to set program (%d) on rhythm is invalid", name, patchNum); +} + +void Part::setProgram(unsigned int patchNum) { + setPatch(&synth->mt32ram.patches[patchNum]); + setTimbre(&synth->mt32ram.timbres[getAbsTimbreNum()].timbre); + + refresh(); + + allSoundOff(); //FIXME:KG: Is this correct? +} + +void Part::backupCacheToPartials(PatchCache cache[4]) { + // check if any partials are still playing with the old patch cache + // if so then duplicate the cached data from the part to the partial so that + // we can change the part's cache without affecting the partial. + // We delay this until now to avoid a copy operation with every note played + for (int m = 0; m < MT32EMU_MAX_POLY; m++) { + for (int i = 0; i < 4; i++) { + Partial *partial = polyTable[m].partials[i]; + if (partial != NULL && partial->patchCache == &cache[i]) { + partial->cachebackup = cache[i]; + partial->patchCache = &partial->cachebackup; + } + } + } +} + +void Part::cacheTimbre(PatchCache cache[4], const TimbreParam *timbre) { + backupCacheToPartials(cache); + int partialCount = 0; + for (int t = 0; t < 4; t++) { + if (((timbre->common.pmute >> t) & 0x1) == 1) { + cache[t].playPartial = true; + partialCount++; + } else { + cache[t].playPartial = false; + continue; + } + + // Calculate and cache common parameters + + cache[t].pcm = timbre->partial[t].wg.pcmwave; + cache[t].useBender = (timbre->partial[t].wg.bender == 1); + + switch (t) { + case 0: + cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x2) ? true : false; + cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12]; + cache[t].structurePosition = 0; + cache[t].structurePair = 1; + break; + case 1: + cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x1) ? true : false; + cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12]; + cache[t].structurePosition = 1; + cache[t].structurePair = 0; + break; + case 2: + cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x2) ? true : false; + cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34]; + cache[t].structurePosition = 0; + cache[t].structurePair = 3; + break; + case 3: + cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x1) ? true : false; + cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34]; + cache[t].structurePosition = 1; + cache[t].structurePair = 2; + break; + default: + break; + } + + cache[t].waveform = timbre->partial[t].wg.waveform; + cache[t].pulsewidth = timbre->partial[t].wg.pulsewid; + cache[t].pwsens = timbre->partial[t].wg.pwvelo; + if (timbre->partial[t].wg.keyfollow > 16) { + synth->printDebug("Bad keyfollow value in timbre!"); + cache[t].pitchKeyfollow = 1.0f; + } else { + cache[t].pitchKeyfollow = floatKeyfollow[timbre->partial[t].wg.keyfollow]; + } + + cache[t].pitch = timbre->partial[t].wg.coarse + (timbre->partial[t].wg.fine - 50) / 100.0f + 24.0f; + cache[t].pitchEnv = timbre->partial[t].env; + cache[t].pitchEnv.sensitivity = (char)((float)cache[t].pitchEnv.sensitivity * 1.27f); + cache[t].pitchsustain = cache[t].pitchEnv.level[3]; + + // Calculate and cache TVA envelope stuff + cache[t].ampEnv = timbre->partial[t].tva; + cache[t].ampEnv.level = (char)((float)cache[t].ampEnv.level * 1.27f); + + cache[t].ampbias[0] = fixBiaslevel(cache[t].ampEnv.biaspoint1, &cache[t].ampdir[0]); + cache[t].ampblevel[0] = 12 - cache[t].ampEnv.biaslevel1; + cache[t].ampbias[1] = fixBiaslevel(cache[t].ampEnv.biaspoint2, &cache[t].ampdir[1]); + cache[t].ampblevel[1] = 12 - cache[t].ampEnv.biaslevel2; + cache[t].ampdepth = cache[t].ampEnv.envvkf * cache[t].ampEnv.envvkf; + + // Calculate and cache filter stuff + cache[t].filtEnv = timbre->partial[t].tvf; + cache[t].filtkeyfollow = fixKeyfollow(cache[t].filtEnv.keyfollow); + cache[t].filtEnv.envdepth = (char)((float)cache[t].filtEnv.envdepth * 1.27); + cache[t].tvfbias = fixBiaslevel(cache[t].filtEnv.biaspoint, &cache[t].tvfdir); + cache[t].tvfblevel = cache[t].filtEnv.biaslevel; + cache[t].filtsustain = cache[t].filtEnv.envlevel[3]; + + // Calculate and cache LFO stuff + cache[t].lfodepth = timbre->partial[t].lfo.depth; + cache[t].lfoperiod = synth->tables.lfoPeriod[(int)timbre->partial[t].lfo.rate]; + cache[t].lforate = timbre->partial[t].lfo.rate; + cache[t].modsense = timbre->partial[t].lfo.modsense; + } + for (int t = 0; t < 4; t++) { + // Common parameters, stored redundantly + cache[t].dirty = false; + cache[t].partialCount = partialCount; + cache[t].sustain = (timbre->common.nosustain == 0); + } + //synth->printDebug("Res 1: %d 2: %d 3: %d 4: %d", cache[0].waveform, cache[1].waveform, cache[2].waveform, cache[3].waveform); + +#if MT32EMU_MONITOR_INSTRUMENTS == 1 + synth->printDebug("%s (%s): Recached timbre", name, currentInstr); + for (int i = 0; i < 4; i++) { + synth->printDebug(" %d: play=%s, pcm=%s (%d), wave=%d", i, cache[i].playPartial ? "YES" : "NO", cache[i].PCMPartial ? "YES" : "NO", timbre->partial[i].wg.pcmwave, timbre->partial[i].wg.waveform); + } +#endif +} + +const char *Part::getName() const { + return name; +} + +void Part::updateVolume() { + volumeMult = synth->tables.volumeMult[patchTemp->outlevel * expression / 127]; +} + +int Part::getVolume() const { + // FIXME: Use the mappings for this in the control ROM + return patchTemp->outlevel * 127 / 100; +} + +void Part::setVolume(int midiVolume) { + // FIXME: Use the mappings for this in the control ROM + patchTemp->outlevel = (Bit8u)(midiVolume * 100 / 127); + updateVolume(); + synth->printDebug("%s (%s): Set volume to %d", name, currentInstr, midiVolume); +} + +void Part::setExpression(int midiExpression) { + expression = midiExpression; + updateVolume(); +} + +void RhythmPart::setPan(unsigned int midiPan) +{ + // FIXME:KG: This is unchangeable for drums (they always use drumPan), is that correct? + synth->printDebug("%s: Setting pan (%d) not supported on rhythm", name, midiPan); +} + +void Part::setPan(unsigned int midiPan) { + // FIXME:KG: Tweaked this a bit so that we have a left 100%, centre and right 100% + // (But this makes the range somewhat skewed) + // Check against the real thing + // NOTE: Panning is inverted compared to GM. + if (midiPan < 64) { + volumesetting.leftvol = (Bit16s)(midiPan * 512); + volumesetting.rightvol = 32767; + } else if (midiPan == 64) { + volumesetting.leftvol = 32767; + volumesetting.rightvol = 32767; + } else { + volumesetting.rightvol = (Bit16s)((127 - midiPan) * 520); + volumesetting.leftvol = 32767; + } + patchTemp->panpot = (Bit8u)(midiPan * 14 / 127); + //synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot); +} + +void RhythmPart::playNote(unsigned int key, int vel) { + if (key < 24 || key > 108)/*> 87 on MT-32)*/ { + synth->printDebug("%s: Attempted to play invalid key %d", name, key); + return; + } + int drumNum = key - 24; + int drumTimbreNum = rhythmTemp[drumNum].timbre; + if (drumTimbreNum >= 127) { // 94 on MT-32 + synth->printDebug("%s: Attempted to play unmapped key %d", name, key); + return; + } + int absTimbreNum = drumTimbreNum + 128; + TimbreParam *timbre = &synth->mt32ram.timbres[absTimbreNum].timbre; + memcpy(currentInstr, timbre->common.name, 10); +#if MT32EMU_MONITOR_INSTRUMENTS == 1 + synth->printDebug("%s (%s): starting poly (drum %d, timbre %d) - Vel %d Key %d", name, currentInstr, drumNum, absTimbreNum, vel, key); +#endif + if (drumCache[drumNum][0].dirty) { + cacheTimbre(drumCache[drumNum], timbre); + } + playPoly(drumCache[drumNum], key, MIDDLEC, vel); +} + +void Part::playNote(unsigned int key, int vel) { + int freqNum = key; + if (freqNum < 12) { + synth->printDebug("%s (%s): Attempted to play invalid key %d < 12; moving up by octave", name, currentInstr, key); + freqNum += 12; + } else if (freqNum > 108) { + synth->printDebug("%s (%s): Attempted to play invalid key %d > 108; moving down by octave", name, currentInstr, key); + while (freqNum > 108) { + freqNum -= 12; + } + } + // POLY1 mode, Single Assign + // Haven't found any software that uses any of the other poly modes + // FIXME:KG: Should this also apply to rhythm? + for (unsigned int i = 0; i < MT32EMU_MAX_POLY; i++) { + if (polyTable[i].isActive() && (polyTable[i].key == key)) { + //AbortPoly(&polyTable[i]); + stopNote(key); + break; + } + } +#if MT32EMU_MONITOR_INSTRUMENTS == 1 + synth->printDebug("%s (%s): starting poly - Vel %d Key %d", name, currentInstr, vel, key); +#endif + if (patchCache[0].dirty) { + cacheTimbre(patchCache, timbreTemp); + } + playPoly(patchCache, key, freqNum, vel); +} + +void Part::playPoly(const PatchCache cache[4], unsigned int key, int freqNum, int vel) { + unsigned int needPartials = cache[0].partialCount; + unsigned int freePartials = synth->partialManager->getFreePartialCount(); + + if (freePartials < needPartials) { + if (!synth->partialManager->freePartials(needPartials - freePartials, partNum)) { + synth->printDebug("%s (%s): Insufficient free partials to play key %d (vel=%d); needed=%d, free=%d", name, currentInstr, key, vel, needPartials, synth->partialManager->getFreePartialCount()); + return; + } + } + // Find free poly + int m; + for (m = 0; m < MT32EMU_MAX_POLY; m++) { + if (!polyTable[m].isActive()) { + break; + } + } + if (m == MT32EMU_MAX_POLY) { + synth->printDebug("%s (%s): No free poly to play key %d (vel %d)", name, currentInstr, key, vel); + return; + } + + dpoly *tpoly = &polyTable[m]; + + tpoly->isPlaying = true; + tpoly->key = key; + tpoly->isDecay = false; + tpoly->freqnum = freqNum; + tpoly->vel = vel; + tpoly->pedalhold = false; + + bool allnull = true; + for (int x = 0; x < 4; x++) { + if (cache[x].playPartial) { + tpoly->partials[x] = synth->partialManager->allocPartial(partNum); + allnull = false; + } else { + tpoly->partials[x] = NULL; + } + } + + if (allnull) + synth->printDebug("%s (%s): No partials to play for this instrument", name, this->currentInstr); + + tpoly->sustain = cache[0].sustain; + tpoly->volumeptr = &volumeMult; + + for (int x = 0; x < 4; x++) { + if (tpoly->partials[x] != NULL) { + tpoly->partials[x]->startPartial(tpoly, &cache[x], tpoly->partials[cache[x].structurePair]); + tpoly->partials[x]->setBend(bend); + } + } +} + +static void startDecayPoly(dpoly *tpoly) { + if (tpoly->isDecay) { + return; + } + tpoly->isDecay = true; + + for (int t = 0; t < 4; t++) { + Partial *partial = tpoly->partials[t]; + if (partial == NULL) + continue; + partial->startDecayAll(); + } + tpoly->isPlaying = false; +} + +void Part::allNotesOff() { + // Note: Unchecked on real MT-32, but the MIDI specification states that all notes off (0x7B) + // should treat the hold pedal as usual. + // All *sound* off (0x78) should stop notes immediately regardless of the hold pedal. + // The latter controller is not implemented on the MT-32 (according to the docs). + for (int q = 0; q < MT32EMU_MAX_POLY; q++) { + dpoly *tpoly = &polyTable[q]; + if (tpoly->isPlaying) { + if (holdpedal) + tpoly->pedalhold = true; + else if (tpoly->sustain) + startDecayPoly(tpoly); + } + } +} + +void Part::allSoundOff() { + for (int q = 0; q < MT32EMU_MAX_POLY; q++) { + dpoly *tpoly = &polyTable[q]; + if (tpoly->isPlaying) { + startDecayPoly(tpoly); + } + } +} + +void Part::stopPedalHold() { + for (int q = 0; q < MT32EMU_MAX_POLY; q++) { + dpoly *tpoly; + tpoly = &polyTable[q]; + if (tpoly->isActive() && tpoly->pedalhold) + stopNote(tpoly->key); + } +} + +void Part::stopNote(unsigned int key) { + // Non-sustaining instruments ignore stop commands. + // They die away eventually anyway + +#if MT32EMU_MONITOR_INSTRUMENTS == 1 + synth->printDebug("%s (%s): stopping key %d", name, currentInstr, key); +#endif + + if (key != 255) { + for (int q = 0; q < MT32EMU_MAX_POLY; q++) { + dpoly *tpoly = &polyTable[q]; + if (tpoly->isPlaying && tpoly->key == key) { + if (holdpedal) + tpoly->pedalhold = true; + else if (tpoly->sustain) + startDecayPoly(tpoly); + } + } + return; + } + + // Find oldest poly... yes, the MT-32 can be reconfigured to kill different poly first + // This is simplest + int oldest = -1; + Bit32u oldage = 0; + + for (int q = 0; q < MT32EMU_MAX_POLY; q++) { + dpoly *tpoly = &polyTable[q]; + + if (tpoly->isPlaying && !tpoly->isDecay) { + if (tpoly->getAge() >= oldage) { + oldage = tpoly->getAge(); + oldest = q; + } + } + } + + if (oldest != -1) { + startDecayPoly(&polyTable[oldest]); + } +} + +} diff --git a/engines/sci/sfx/softseq/mt32/part.h b/engines/sci/sfx/softseq/mt32/part.h new file mode 100644 index 0000000000..54c4999653 --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/part.h @@ -0,0 +1,113 @@ +/* Copyright (c) 2003-2005 Various contributors + * + * 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. + */ + +#ifndef MT32EMU_PART_H +#define MT32EMU_PART_H + +namespace MT32Emu { + +class PartialManager; +class Synth; + +class Part { +private: + // Pointers to the areas of the MT-32's memory dedicated to this part (for parts 1-8) + MemParams::PatchTemp *patchTemp; + TimbreParam *timbreTemp; + + // 0=Part 1, .. 7=Part 8, 8=Rhythm + unsigned int partNum; + + bool holdpedal; + + StereoVolume volumesetting; + + PatchCache patchCache[4]; + + float bend; // -1.0 .. +1.0 + + dpoly polyTable[MT32EMU_MAX_POLY]; + + void abortPoly(dpoly *poly); + + static int fixKeyfollow(int srckey); + static int fixBiaslevel(int srcpnt, int *dir); + + void setPatch(const PatchParam *patch); + +protected: + Synth *synth; + char name[8]; // "Part 1".."Part 8", "Rhythm" + char currentInstr[11]; + int expression; + Bit32u volumeMult; + + void updateVolume(); + void backupCacheToPartials(PatchCache cache[4]); + void cacheTimbre(PatchCache cache[4], const TimbreParam *timbre); + void playPoly(const PatchCache cache[4], unsigned int key, int freqNum, int vel); + const char *getName() const; + +public: + Part(Synth *synth, unsigned int usePartNum); + virtual ~Part() {} + virtual void playNote(unsigned int key, int vel); + void stopNote(unsigned int key); + void allNotesOff(); + void allSoundOff(); + int getVolume() const; + void setVolume(int midiVolume); + void setExpression(int midiExpression); + virtual void setPan(unsigned int midiPan); + virtual void setBend(unsigned int midiBend); + virtual void setModulation(unsigned int midiModulation); + virtual void setProgram(unsigned int midiProgram); + void setHoldPedal(bool pedalval); + void stopPedalHold(); + virtual void refresh(); + virtual void refreshTimbre(unsigned int absTimbreNum); + virtual void setTimbre(TimbreParam *timbre); + virtual unsigned int getAbsTimbreNum() const; + const char *getCurrentInstr() const; +}; + +class RhythmPart: public Part { + // Pointer to the area of the MT-32's memory dedicated to rhythm + const MemParams::RhythmTemp *rhythmTemp; + + // This caches the timbres/settings in use by the rhythm part + PatchCache drumCache[85][4]; + StereoVolume drumPan[85]; +public: + RhythmPart(Synth *synth, unsigned int usePartNum); + void refresh(); + void refreshTimbre(unsigned int timbreNum); + void setTimbre(TimbreParam *timbre); + void playNote(unsigned int key, int vel); + unsigned int getAbsTimbreNum() const; + void setPan(unsigned int midiPan); + void setBend(unsigned int midiBend); + void setModulation(unsigned int midiModulation); + void setProgram(unsigned int patchNum); +}; + +} +#endif diff --git a/engines/sci/sfx/softseq/mt32/partial.cpp b/engines/sci/sfx/softseq/mt32/partial.cpp new file mode 100644 index 0000000000..9d32282a82 --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/partial.cpp @@ -0,0 +1,960 @@ +/* Copyright (c) 2003-2005 Various contributors + * + * 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. + */ + +#include +#include +#include + +#include "mt32emu.h" + +#ifdef MACOSX +// Older versions of Mac OS X didn't supply a powf function. To ensure +// binary compatibility, we force using pow instead of powf (the only +// potential drawback is that it might be a little bit slower). +#define powf pow +#endif + +#define FIXEDPOINT_UDIV(x, y, point) (((x) << (point)) / ((y))) +#define FIXEDPOINT_SDIV(x, y, point) (((x) * (1 << point)) / ((y))) +#define FIXEDPOINT_UMULT(x, y, point) (((x) * (y)) >> point) +#define FIXEDPOINT_SMULT(x, y, point) (((x) * (y)) / (1 << point)) + +using namespace MT32Emu; + +Partial::Partial(Synth *useSynth) { + this->synth = useSynth; + ownerPart = -1; + poly = NULL; + pair = NULL; +#if MT32EMU_ACCURATENOTES == 1 + for (int i = 0; i < 3; i++) { + noteLookupStorage.waveforms[i] = new Bit16s[65536]; + } + noteLookup = ¬eLookupStorage; +#endif +} + +Partial::~Partial() { +#if MT32EMU_ACCURATENOTES == 1 + for (int i = 0; i < 3; i++) { + delete[] noteLookupStorage.waveforms[i]; + } + delete[] noteLookupStorage.wavTable; +#endif +} + +int Partial::getOwnerPart() const { + return ownerPart; +} + +bool Partial::isActive() { + return ownerPart > -1; +} + +const dpoly *Partial::getDpoly() const { + return this->poly; +} + +void Partial::activate(int part) { + // This just marks the partial as being assigned to a part + ownerPart = part; +} + +void Partial::deactivate() { + ownerPart = -1; + if (poly != NULL) { + for (int i = 0; i < 4; i++) { + if (poly->partials[i] == this) { + poly->partials[i] = NULL; + break; + } + } + if (pair != NULL) { + pair->pair = NULL; + } + } +} + +void Partial::initKeyFollow(int key) { + // Setup partial keyfollow + // Note follow relative to middle C + + // Calculate keyfollow for pitch +#if 1 + float rel = key == -1 ? 0.0f : (key - MIDDLEC); + float newPitch = rel * patchCache->pitchKeyfollow + patchCache->pitch + patchCache->pitchShift; + //FIXME:KG: Does it truncate the keyfollowed pitch to a semitone (towards MIDDLEC)? + //int newKey = (int)(rel * patchCache->pitchKeyfollow); + //float newPitch = newKey + patchCache->pitch + patchCache->pitchShift; +#else + float rel = key == -1 ? 0.0f : (key + patchCache->pitchShift - MIDDLEC); + float newPitch = rel * patchCache->pitchKeyfollow + patchCache->pitch; +#endif +#if MT32EMU_ACCURATENOTES == 1 + noteVal = newPitch; + synth->printDebug("key=%d, pitch=%f, pitchKeyfollow=%f, pitchShift=%f, newPitch=%f", key, patchCache->pitch, patchCache->pitchKeyfollow, patchCache->pitchShift, newPitch); +#else + float newPitchInt; + float newPitchFract = modff(newPitch, &newPitchInt); + if (newPitchFract > 0.5f) { + newPitchInt += 1.0f; + newPitchFract -= 1.0f; + } + noteVal = (int)newPitchInt; + fineShift = (int)(powf(2.0f, newPitchFract / 12.0f) * 4096.0f); + synth->printDebug("key=%d, pitch=%f, pitchKeyfollow=%f, pitchShift=%f, newPitch=%f, noteVal=%d, fineShift=%d", key, patchCache->pitch, patchCache->pitchKeyfollow, patchCache->pitchShift, newPitch, noteVal, fineShift); +#endif + // FIXME:KG: Raise/lower by octaves until in the supported range. + while (noteVal > HIGHEST_NOTE) // FIXME:KG: see tables.cpp: >108? + noteVal -= 12; + while (noteVal < LOWEST_NOTE) // FIXME:KG: see tables.cpp: <12? + noteVal += 12; + // Calculate keyfollow for filter + int keyfollow = ((key - MIDDLEC) * patchCache->filtkeyfollow) / 4096; + if (keyfollow > 108) + keyfollow = 108; + else if (keyfollow < -108) + keyfollow = -108; + filtVal = synth->tables.tvfKeyfollowMult[keyfollow + 108]; + realVal = synth->tables.tvfKeyfollowMult[(noteVal - MIDDLEC) + 108]; +} + +int Partial::getKey() const { + if (poly == NULL) { + return -1; + } else { + return poly->key; + } +} + +void Partial::startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial) { + if (usePoly == NULL || useCache == NULL) { + synth->printDebug("*** Error: Starting partial for owner %d, usePoly=%s, useCache=%s", ownerPart, usePoly == NULL ? "*** NULL ***" : "OK", useCache == NULL ? "*** NULL ***" : "OK"); + return; + } + patchCache = useCache; + poly = usePoly; + mixType = patchCache->structureMix; + structurePosition = patchCache->structurePosition; + + play = true; + initKeyFollow(poly->freqnum); // Initialises noteVal, filtVal and realVal +#if MT32EMU_ACCURATENOTES == 0 + noteLookup = &synth->tables.noteLookups[noteVal - LOWEST_NOTE]; +#else + Tables::initNote(synth, ¬eLookupStorage, noteVal, (float)synth->myProp.sampleRate, synth->masterTune, synth->pcmWaves, NULL); +#endif + keyLookup = &synth->tables.keyLookups[poly->freqnum - 12]; + + if (patchCache->PCMPartial) { + pcmNum = patchCache->pcm; + if (synth->controlROMMap->pcmCount > 128) { + // CM-32L, etc. support two "banks" of PCMs, selectable by waveform type parameter. + if (patchCache->waveform > 1) { + pcmNum += 128; + } + } + pcmWave = &synth->pcmWaves[pcmNum]; + } else { + pcmWave = NULL; + } + + lfoPos = 0; + pulsewidth = patchCache->pulsewidth + synth->tables.pwVelfollowAdd[patchCache->pwsens][poly->vel]; + if (pulsewidth > 100) { + pulsewidth = 100; + } else if (pulsewidth < 0) { + pulsewidth = 0; + } + + for (int e = 0; e < 3; e++) { + envs[e].envpos = 0; + envs[e].envstat = -1; + envs[e].envbase = 0; + envs[e].envdist = 0; + envs[e].envsize = 0; + envs[e].sustaining = false; + envs[e].decaying = false; + envs[e].prevlevel = 0; + envs[e].counter = 0; + envs[e].count = 0; + } + ampEnvVal = 0; + pitchEnvVal = 0; + pitchSustain = false; + loopPos = 0; + partialOff.pcmoffset = partialOff.pcmplace = 0; + pair = pairPartial; + useNoisePair = pairPartial == NULL && (mixType == 1 || mixType == 2); + age = 0; + alreadyOutputed = false; + memset(history,0,sizeof(history)); +} + +Bit16s *Partial::generateSamples(long length) { + if (!isActive() || alreadyOutputed) { + return NULL; + } + if (poly == NULL) { + synth->printDebug("*** ERROR: poly is NULL at Partial::generateSamples()!"); + return NULL; + } + + alreadyOutputed = true; + + // Generate samples + + Bit16s *partialBuf = &myBuffer[0]; + Bit32u volume = *poly->volumeptr; + while (length--) { + Bit32s envval; + Bit32s sample = 0; + if (!envs[EnvelopeType_amp].sustaining) { + if (envs[EnvelopeType_amp].count <= 0) { + Bit32u ampval = getAmpEnvelope(); + if (!play) { + deactivate(); + break; + } + if (ampval > 100) { + ampval = 100; + } + + ampval = synth->tables.volumeMult[ampval]; + ampval = FIXEDPOINT_UMULT(ampval, synth->tables.tvaVelfollowMult[poly->vel][(int)patchCache->ampEnv.velosens], 8); + //if (envs[EnvelopeType_amp].sustaining) + ampEnvVal = ampval; + } + --envs[EnvelopeType_amp].count; + } + + unsigned int lfoShift = 0x1000; + if (pitchSustain) { + // Calculate LFO position + // LFO does not kick in completely until pitch envelope sustains + if (patchCache->lfodepth > 0) { + lfoPos++; + if (lfoPos >= patchCache->lfoperiod) + lfoPos = 0; + int lfoatm = FIXEDPOINT_UDIV(lfoPos, patchCache->lfoperiod, 16); + int lfoatr = synth->tables.sintable[lfoatm]; + lfoShift = synth->tables.lfoShift[patchCache->lfodepth][lfoatr]; + } + } else { + // Calculate Pitch envelope + envval = getPitchEnvelope(); + int pd = patchCache->pitchEnv.depth; + pitchEnvVal = synth->tables.pitchEnvVal[pd][envval]; + } + + int delta; + + // Wrap positions or end if necessary + if (patchCache->PCMPartial) { + // PCM partial + + delta = noteLookup->wavTable[pcmNum]; + int len = pcmWave->len; + if (partialOff.pcmplace >= len) { + if (pcmWave->loop) { + //partialOff.pcmplace = partialOff.pcmoffset = 0; + partialOff.pcmplace %= len; + } else { + play = false; + deactivate(); + break; + } + } + } else { + // Synthesis partial + delta = 0x10000; + partialOff.pcmplace %= (Bit16u)noteLookup->div2; + } + + // Build delta for position of next sample + // Fix delta code + Bit32u tdelta = delta; +#if MT32EMU_ACCURATENOTES == 0 + tdelta = FIXEDPOINT_UMULT(tdelta, fineShift, 12); +#endif + tdelta = FIXEDPOINT_UMULT(tdelta, pitchEnvVal, 12); + tdelta = FIXEDPOINT_UMULT(tdelta, lfoShift, 12); + tdelta = FIXEDPOINT_UMULT(tdelta, bendShift, 12); + delta = (int)tdelta; + + // Get waveform - either PCM or synthesized sawtooth or square + if (ampEnvVal > 0) { + if (patchCache->PCMPartial) { + // Render PCM sample + int ra, rb, dist; + Bit32u taddr; + Bit32u pcmAddr = pcmWave->addr; + if (delta < 0x10000) { + // Linear sound interpolation + taddr = pcmAddr + partialOff.pcmplace; + ra = synth->pcmROMData[taddr]; + taddr++; + if (taddr == pcmAddr + pcmWave->len) { + // Past end of PCM + if (pcmWave->loop) { + rb = synth->pcmROMData[pcmAddr]; + } else { + rb = 0; + } + } else { + rb = synth->pcmROMData[taddr]; + } + dist = rb - ra; + sample = (ra + ((dist * (Bit32s)(partialOff.pcmoffset >> 8)) >> 8)); + } else { + // Sound decimation + // The right way to do it is to use a lowpass filter on the waveform before selecting + // a point. This is too slow. The following approximates this as fast as possible + int idelta = delta >> 16; + taddr = pcmAddr + partialOff.pcmplace; + ra = synth->pcmROMData[taddr++]; + for (int ix = 0; ix < idelta - 1; ix++) { + if (taddr == pcmAddr + pcmWave->len) { + // Past end of PCM + if (pcmWave->loop) { + taddr = pcmAddr; + } else { + // Behave as if all subsequent samples were 0 + break; + } + } + ra += synth->pcmROMData[taddr++]; + } + sample = ra / idelta; + } + } else { + // Render synthesised sample + int toff = partialOff.pcmplace; + int minorplace = partialOff.pcmoffset >> 14; + Bit32s filterInput; + Bit32s filtval = getFiltEnvelope(); + + //synth->printDebug("Filtval: %d", filtval); + + if ((patchCache->waveform & 1) == 0) { + // Square waveform. Made by combining two pregenerated bandlimited + // sawtooth waveforms + Bit32u ofsA = ((toff << 2) + minorplace) % noteLookup->waveformSize[0]; + int width = FIXEDPOINT_UMULT(noteLookup->div2, synth->tables.pwFactor[pulsewidth], 7); + Bit32u ofsB = (ofsA + width) % noteLookup->waveformSize[0]; + Bit16s pa = noteLookup->waveforms[0][ofsA]; + Bit16s pb = noteLookup->waveforms[0][ofsB]; + filterInput = pa - pb; + // Non-bandlimited squarewave + /* + ofs = FIXEDPOINT_UMULT(noteLookup->div2, synth->tables.pwFactor[patchCache->pulsewidth], 8); + if (toff < ofs) + sample = 1 * WGAMP; + else + sample = -1 * WGAMP; + */ + } else { + // Sawtooth. Made by combining the full cosine and half cosine according + // to how it looks on the MT-32. What it really does it takes the + // square wave and multiplies it by a full cosine + int waveoff = (toff << 2) + minorplace; + if (toff < noteLookup->sawTable[pulsewidth]) + filterInput = noteLookup->waveforms[1][waveoff % noteLookup->waveformSize[1]]; + else + filterInput = noteLookup->waveforms[2][waveoff % noteLookup->waveformSize[2]]; + // This is the correct way + // Seems slow to me (though bandlimited) -- doesn't seem to + // sound any better though + /* + //int pw = (patchCache->pulsewidth * pulsemod[filtval]) >> 8; + + Bit32u ofs = toff % (noteLookup->div2 >> 1); + + Bit32u ofs3 = toff + FIXEDPOINT_UMULT(noteLookup->div2, synth->tables.pwFactor[patchCache->pulsewidth], 9); + ofs3 = ofs3 % (noteLookup->div2 >> 1); + + pa = noteLookup->waveforms[0][ofs]; + pb = noteLookup->waveforms[0][ofs3]; + sample = ((pa - pb) * noteLookup->waveforms[2][toff]) / 2; + */ + } + + //Very exact filter + if (filtval > ((FILTERGRAN * 15) / 16)) + filtval = ((FILTERGRAN * 15) / 16); + sample = (Bit32s)(floorf((synth->iirFilter)((float)filterInput, &history[0], synth->tables.filtCoeff[filtval][(int)patchCache->filtEnv.resonance])) / synth->tables.resonanceFactor[patchCache->filtEnv.resonance]); + if (sample < -32768) { + synth->printDebug("Overdriven amplitude for %d: %d:=%d < -32768", patchCache->waveform, filterInput, sample); + sample = -32768; + } + else if (sample > 32767) { + synth->printDebug("Overdriven amplitude for %d: %d:=%d > 32767", patchCache->waveform, filterInput, sample); + sample = 32767; + } + } + } + + // Add calculated delta to our waveform offset + Bit32u absOff = ((partialOff.pcmplace << 16) | partialOff.pcmoffset); + absOff += delta; + partialOff.pcmplace = (Bit16u)((absOff & 0xFFFF0000) >> 16); + partialOff.pcmoffset = (Bit16u)(absOff & 0xFFFF); + + // Put volume envelope over generated sample + sample = FIXEDPOINT_SMULT(sample, ampEnvVal, 9); + sample = FIXEDPOINT_SMULT(sample, volume, 7); + envs[EnvelopeType_amp].envpos++; + envs[EnvelopeType_pitch].envpos++; + envs[EnvelopeType_filt].envpos++; + + *partialBuf++ = (Bit16s)sample; + } + // We may have deactivated and broken out of the loop before the end of the buffer, + // if so then fill the remainder with 0s. + if (++length > 0) + memset(partialBuf, 0, length * 2); + return &myBuffer[0]; +} + +void Partial::setBend(float factor) { + if (!patchCache->useBender || factor == 0.0f) { + bendShift = 4096; + return; + } + // NOTE:KG: We can't do this smoothly with lookup tables, unless we use several MB. + // FIXME:KG: Bend should be influenced by pitch key-follow too, according to docs. + float bendSemitones = factor * patchCache->benderRange; // -24 .. 24 + float mult = powf(2.0f, bendSemitones / 12.0f); + synth->printDebug("setBend(): factor=%f, benderRange=%f, semitones=%f, mult=%f\n", factor, patchCache->benderRange, bendSemitones, mult); + bendShift = (int)(mult * 4096.0f); +} + +Bit16s *Partial::mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) { + if (buf1 == NULL) + return buf2; + if (buf2 == NULL) + return buf1; + + Bit16s *outBuf = buf1; +#if MT32EMU_USE_MMX >= 1 + // KG: This seems to be fine + int donelen = i386_mixBuffers(buf1, buf2, len); + len -= donelen; + buf1 += donelen; + buf2 += donelen; +#endif + while (len--) { + *buf1 = *buf1 + *buf2; + buf1++, buf2++; + } + return outBuf; +} + +Bit16s *Partial::mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) { + if (buf1 == NULL) + return NULL; + if (buf2 == NULL) { + Bit16s *outBuf = buf1; + while (len--) { + if (*buf1 < -8192) + *buf1 = -8192; + else if (*buf1 > 8192) + *buf1 = 8192; + buf1++; + } + return outBuf; + } + + Bit16s *outBuf = buf1; +#if MT32EMU_USE_MMX >= 1 + // KG: This seems to be fine + int donelen = i386_mixBuffersRingMix(buf1, buf2, len); + len -= donelen; + buf1 += donelen; + buf2 += donelen; +#endif + while (len--) { + float a, b; + a = ((float)*buf1) / 8192.0f; + b = ((float)*buf2) / 8192.0f; + a = (a * b) + a; + if (a>1.0) + a = 1.0; + if (a<-1.0) + a = -1.0; + *buf1 = (Bit16s)(a * 8192.0f); + buf1++; + buf2++; + //buf1[i] = (Bit16s)(((Bit32s)buf1[i] * (Bit32s)buf2[i]) >> 10) + buf1[i]; + } + return outBuf; +} + +Bit16s *Partial::mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) { + if (buf1 == NULL) { + return NULL; + } + if (buf2 == NULL) { + return NULL; + } + + Bit16s *outBuf = buf1; +#if MT32EMU_USE_MMX >= 1 + // FIXME:KG: Not really checked as working + int donelen = i386_mixBuffersRing(buf1, buf2, len); + len -= donelen; + buf1 += donelen; + buf2 += donelen; +#endif + while (len--) { + float a, b; + a = ((float)*buf1) / 8192.0f; + b = ((float)*buf2) / 8192.0f; + a *= b; + if (a>1.0) + a = 1.0; + if (a<-1.0) + a = -1.0; + *buf1 = (Bit16s)(a * 8192.0f); + buf1++; + buf2++; + } + return outBuf; +} + +void Partial::mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len) { + if (buf2 == NULL) { + while (len--) { + *outBuf++ = *buf1++; + *outBuf++ = 0; + } + } else if (buf1 == NULL) { + while (len--) { + *outBuf++ = 0; + *outBuf++ = *buf2++; + } + } else { + while (len--) { + *outBuf++ = *buf1++; + *outBuf++ = *buf2++; + } + } +} + +bool Partial::produceOutput(Bit16s *partialBuf, long length) { + if (!isActive() || alreadyOutputed) + return false; + if (poly == NULL) { + synth->printDebug("*** ERROR: poly is NULL at Partial::produceOutput()!"); + return false; + } + + Bit16s *pairBuf = NULL; + // Check for dependant partial + if (pair != NULL) { + if (!pair->alreadyOutputed) { + // Note: pair may have become NULL after this + pairBuf = pair->generateSamples(length); + } + } else if (useNoisePair) { + // Generate noise for pairless ring mix + pairBuf = synth->tables.noiseBuf; + } + + Bit16s *myBuf = generateSamples(length); + + if (myBuf == NULL && pairBuf == NULL) + return false; + + Bit16s *p1buf, *p2buf; + + if (structurePosition == 0 || pairBuf == NULL) { + p1buf = myBuf; + p2buf = pairBuf; + } else { + p2buf = myBuf; + p1buf = pairBuf; + } + + //synth->printDebug("mixType: %d", mixType); + + Bit16s *mixedBuf; + switch (mixType) { + case 0: + // Standard sound mix + mixedBuf = mixBuffers(p1buf, p2buf, length); + break; + + case 1: + // Ring modulation with sound mix + mixedBuf = mixBuffersRingMix(p1buf, p2buf, length); + break; + + case 2: + // Ring modulation alone + mixedBuf = mixBuffersRing(p1buf, p2buf, length); + break; + + case 3: + // Stereo mixing. One partial to one speaker channel, one to another. + // FIXME:KG: Surely we should be multiplying by the left/right volumes here? + mixBuffersStereo(p1buf, p2buf, partialBuf, length); + return true; + + default: + mixedBuf = mixBuffers(p1buf, p2buf, length); + break; + } + + if (mixedBuf == NULL) + return false; + + Bit16s leftvol, rightvol; + leftvol = patchCache->pansetptr->leftvol; + rightvol = patchCache->pansetptr->rightvol; + +#if MT32EMU_USE_MMX >= 2 + // FIXME:KG: This appears to introduce crackle + int donelen = i386_partialProductOutput(length, leftvol, rightvol, partialBuf, mixedBuf); + length -= donelen; + mixedBuf += donelen; + partialBuf += donelen * 2; +#endif + while (length--) { + *partialBuf++ = (Bit16s)(((Bit32s)*mixedBuf * (Bit32s)leftvol) >> 15); + *partialBuf++ = (Bit16s)(((Bit32s)*mixedBuf * (Bit32s)rightvol) >> 15); + mixedBuf++; + } + return true; +} + +Bit32s Partial::getFiltEnvelope() { + int reshigh; + + int cutoff, depth; + + EnvelopeStatus *tStat = &envs[EnvelopeType_filt]; + + if (tStat->decaying) { + reshigh = tStat->envbase; + reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize)); + if (tStat->envpos >= tStat->envsize) + reshigh = 0; + } else { + if (tStat->envstat==4) { + reshigh = patchCache->filtsustain; + if (!poly->sustain) { + startDecay(EnvelopeType_filt, reshigh); + } + } else { + if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) { + if (tStat->envstat==-1) + tStat->envbase = 0; + else + tStat->envbase = patchCache->filtEnv.envlevel[tStat->envstat]; + tStat->envstat++; + tStat->envpos = 0; + if (tStat->envstat == 3) { + tStat->envsize = synth->tables.envTime[(int)patchCache->filtEnv.envtime[tStat->envstat]]; + } else { + Bit32u envTime = (int)patchCache->filtEnv.envtime[tStat->envstat]; + if (tStat->envstat > 1) { + int envDiff = abs(patchCache->filtEnv.envlevel[tStat->envstat] - patchCache->filtEnv.envlevel[tStat->envstat - 1]); + if (envTime > synth->tables.envDeltaMaxTime[envDiff]) { + envTime = synth->tables.envDeltaMaxTime[envDiff]; + } + } + + tStat->envsize = (synth->tables.envTime[envTime] * keyLookup->envTimeMult[(int)patchCache->filtEnv.envtkf]) >> 8; + } + + tStat->envsize++; + tStat->envdist = patchCache->filtEnv.envlevel[tStat->envstat] - tStat->envbase; + } + + reshigh = tStat->envbase; + reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize)); + + } + tStat->prevlevel = reshigh; + } + + cutoff = patchCache->filtEnv.cutoff; + + //if (patchCache->waveform==1) reshigh = (reshigh * 3) >> 2; + + depth = patchCache->filtEnv.envdepth; + + //int sensedep = (depth * 127-patchCache->filtEnv.envsense) >> 7; + depth = FIXEDPOINT_UMULT(depth, synth->tables.tvfVelfollowMult[poly->vel][(int)patchCache->filtEnv.envsense], 8); + + int bias = patchCache->tvfbias; + int dist; + + if (bias != 0) { + //FIXME:KG: Is this really based on pitch (as now), or key pressed? + //synth->printDebug("Cutoff before %d", cutoff); + if (patchCache->tvfdir == 0) { + if (noteVal < bias) { + dist = bias - noteVal; + cutoff = FIXEDPOINT_UMULT(cutoff, synth->tables.tvfBiasMult[patchCache->tvfblevel][dist], 8); + } + } else { + // > Bias + if (noteVal > bias) { + dist = noteVal - bias; + cutoff = FIXEDPOINT_UMULT(cutoff, synth->tables.tvfBiasMult[patchCache->tvfblevel][dist], 8); + } + + } + //synth->printDebug("Cutoff after %d", cutoff); + } + + depth = (depth * keyLookup->envDepthMult[patchCache->filtEnv.envdkf]) >> 8; + reshigh = (reshigh * depth) >> 7; + + Bit32s tmp; + + cutoff *= filtVal; + cutoff /= realVal; //FIXME:KG: With filter keyfollow 0, this makes no sense. What's correct? + + reshigh *= filtVal; + reshigh /= realVal; //FIXME:KG: As above for cutoff + + if (patchCache->waveform == 1) { + reshigh = (reshigh * 65) / 100; + } + + if (cutoff > 100) + cutoff = 100; + else if (cutoff < 0) + cutoff = 0; + if (reshigh > 100) + reshigh = 100; + else if (reshigh < 0) + reshigh = 0; + tmp = noteLookup->nfiltTable[cutoff][reshigh]; + //tmp *= keyfollow; + //tmp /= realfollow; + + //synth->printDebug("Cutoff %d, tmp %d, freq %d", cutoff, tmp, tmp * 256); + return tmp; +} + +bool Partial::shouldReverb() { + if (!isActive()) + return false; + return patchCache->reverb; +} + +Bit32u Partial::getAmpEnvelope() { + Bit32s tc; + + EnvelopeStatus *tStat = &envs[EnvelopeType_amp]; + + if (!play) + return 0; + + if (tStat->decaying) { + tc = tStat->envbase; + tc += (tStat->envdist * tStat->envpos) / tStat->envsize; + if (tc < 0) + tc = 0; + if ((tStat->envpos >= tStat->envsize) || (tc == 0)) { + play = false; + // Don't have to worry about prevlevel storage or anything, this partial's about to die + return 0; + } + } else { + if ((tStat->envstat == -1) || (tStat->envpos >= tStat->envsize)) { + if (tStat->envstat == -1) + tStat->envbase = 0; + else + tStat->envbase = patchCache->ampEnv.envlevel[tStat->envstat]; + tStat->envstat++; + tStat->envpos = 0; + if (tStat->envstat == 4) { + //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize); + tc = patchCache->ampEnv.envlevel[3]; + if (!poly->sustain) + startDecay(EnvelopeType_amp, tc); + else + tStat->sustaining = true; + goto PastCalc; + } + Bit8u targetLevel = patchCache->ampEnv.envlevel[tStat->envstat]; + tStat->envdist = targetLevel - tStat->envbase; + Bit32u envTime = patchCache->ampEnv.envtime[tStat->envstat]; + if (targetLevel == 0) { + tStat->envsize = synth->tables.envDecayTime[envTime]; + } else { + int envLevelDelta = abs(tStat->envdist); + if (envTime > synth->tables.envDeltaMaxTime[envLevelDelta]) { + envTime = synth->tables.envDeltaMaxTime[envLevelDelta]; + } + tStat->envsize = synth->tables.envTime[envTime]; + } + + // Time keyfollow is used by all sections of the envelope (confirmed on CM-32L) + tStat->envsize = FIXEDPOINT_UMULT(tStat->envsize, keyLookup->envTimeMult[(int)patchCache->ampEnv.envtkf], 8); + + switch (tStat->envstat) { + case 0: + //Spot for velocity time follow + //Only used for first attack + tStat->envsize = FIXEDPOINT_UMULT(tStat->envsize, synth->tables.envTimeVelfollowMult[(int)patchCache->ampEnv.envvkf][poly->vel], 8); + //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize); + break; + case 1: + case 2: + case 3: + //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize); + break; + default: + synth->printDebug("Invalid TVA envelope number %d hit!", tStat->envstat); + break; + } + + tStat->envsize++; + + if (tStat->envdist != 0) { + tStat->counter = abs(tStat->envsize / tStat->envdist); + //synth->printDebug("Pos %d, envsize %d envdist %d", tStat->envstat, tStat->envsize, tStat->envdist); + } else { + tStat->counter = 0; + //synth->printDebug("Pos %d, envsize %d envdist %d", tStat->envstat, tStat->envsize, tStat->envdist); + } + } + tc = tStat->envbase; + tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize)); + tStat->count = tStat->counter; +PastCalc: + tc = (tc * (Bit32s)patchCache->ampEnv.level) / 100; + } + + // Prevlevel storage is bottle neck + tStat->prevlevel = tc; + + //Bias level crap stuff now + + for (int i = 0; i < 2; i++) { + if (patchCache->ampblevel[i]!=0) { + int bias = patchCache->ampbias[i]; + if (patchCache->ampdir[i]==0) { + // < Bias + if (noteVal < bias) { + int dist = bias - noteVal; + tc = FIXEDPOINT_UMULT(tc, synth->tables.tvaBiasMult[patchCache->ampblevel[i]][dist], 8); + } + } else { + // > Bias + if (noteVal > bias) { + int dist = noteVal - bias; + tc = FIXEDPOINT_UMULT(tc, synth->tables.tvaBiasMult[patchCache->ampblevel[i]][dist], 8); + } + } + } + } + if (tc < 0) { + synth->printDebug("*** ERROR: tc < 0 (%d) at getAmpEnvelope()", tc); + tc = 0; + } + return (Bit32u)tc; +} + +Bit32s Partial::getPitchEnvelope() { + EnvelopeStatus *tStat = &envs[EnvelopeType_pitch]; + + Bit32s tc; + pitchSustain = false; + if (tStat->decaying) { + if (tStat->envpos >= tStat->envsize) + tc = patchCache->pitchEnv.level[4]; + else { + tc = tStat->envbase; + tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize)); + } + } else { + if (tStat->envstat==3) { + tc = patchCache->pitchsustain; + if (poly->sustain) + pitchSustain = true; + else + startDecay(EnvelopeType_pitch, tc); + } else { + if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) { + tStat->envstat++; + + tStat->envbase = patchCache->pitchEnv.level[tStat->envstat]; + + Bit32u envTime = patchCache->pitchEnv.time[tStat->envstat]; + int envDiff = abs(patchCache->pitchEnv.level[tStat->envstat] - patchCache->pitchEnv.level[tStat->envstat + 1]); + if (envTime > synth->tables.envDeltaMaxTime[envDiff]) { + envTime = synth->tables.envDeltaMaxTime[envDiff]; + } + + tStat->envsize = (synth->tables.envTime[envTime] * keyLookup->envTimeMult[(int)patchCache->pitchEnv.timekeyfollow]) >> 8; + + tStat->envpos = 0; + tStat->envsize++; + tStat->envdist = patchCache->pitchEnv.level[tStat->envstat + 1] - tStat->envbase; + } + tc = tStat->envbase; + tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize)); + } + tStat->prevlevel = tc; + } + return tc; +} + +void Partial::startDecayAll() { + startDecay(EnvelopeType_amp, envs[EnvelopeType_amp].prevlevel); + startDecay(EnvelopeType_filt, envs[EnvelopeType_filt].prevlevel); + startDecay(EnvelopeType_pitch, envs[EnvelopeType_pitch].prevlevel); + pitchSustain = false; +} + +void Partial::startDecay(EnvelopeType envnum, Bit32s startval) { + EnvelopeStatus *tStat = &envs[envnum]; + + tStat->sustaining = false; + tStat->decaying = true; + tStat->envpos = 0; + tStat->envbase = startval; + + switch (envnum) { + case EnvelopeType_amp: + tStat->envsize = FIXEDPOINT_UMULT(synth->tables.envDecayTime[(int)patchCache->ampEnv.envtime[4]], keyLookup->envTimeMult[(int)patchCache->ampEnv.envtkf], 8); + tStat->envdist = -startval; + break; + case EnvelopeType_filt: + tStat->envsize = FIXEDPOINT_UMULT(synth->tables.envDecayTime[(int)patchCache->filtEnv.envtime[4]], keyLookup->envTimeMult[(int)patchCache->filtEnv.envtkf], 8); + tStat->envdist = -startval; + break; + case EnvelopeType_pitch: + tStat->envsize = FIXEDPOINT_UMULT(synth->tables.envDecayTime[(int)patchCache->pitchEnv.time[3]], keyLookup->envTimeMult[(int)patchCache->pitchEnv.timekeyfollow], 8); + tStat->envdist = patchCache->pitchEnv.level[4] - startval; + break; + default: + break; + } + tStat->envsize++; +} diff --git a/engines/sci/sfx/softseq/mt32/partial.h b/engines/sci/sfx/softseq/mt32/partial.h new file mode 100644 index 0000000000..93d8bcd985 --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/partial.h @@ -0,0 +1,148 @@ +/* Copyright (c) 2003-2005 Various contributors + * + * 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. + */ + +#ifndef MT32EMU_PARTIAL_H +#define MT32EMU_PARTIAL_H + +namespace MT32Emu { + +class Synth; +struct NoteLookup; + +enum EnvelopeType { + EnvelopeType_amp = 0, + EnvelopeType_filt = 1, + EnvelopeType_pitch = 2 +}; + +struct EnvelopeStatus { + Bit32s envpos; + Bit32s envstat; + Bit32s envbase; + Bit32s envdist; + Bit32s envsize; + + bool sustaining; + bool decaying; + Bit32s prevlevel; + + Bit32s counter; + Bit32s count; +}; + +// Class definition of MT-32 partials. 32 in all. +class Partial { +private: + Synth *synth; + + int ownerPart; // -1 if unassigned + int mixType; + int structurePosition; // 0 or 1 of a structure pair + bool useNoisePair; + + Bit16s myBuffer[MAX_SAMPLE_OUTPUT]; + + // Keyfollowed note value +#if MT32EMU_ACCURATENOTES == 1 + NoteLookup noteLookupStorage; + float noteVal; +#else + int noteVal; + int fineShift; +#endif + const NoteLookup *noteLookup; // LUTs for this noteVal + const KeyLookup *keyLookup; // LUTs for the clamped (12..108) key + + // Keyfollowed filter values + int realVal; + int filtVal; + + // Only used for PCM partials + int pcmNum; + PCMWaveEntry *pcmWave; + + int pulsewidth; + + Bit32u lfoPos; + soundaddr partialOff; + + Bit32u ampEnvVal; + Bit32u pitchEnvVal; + + float history[32]; + + bool pitchSustain; + + int loopPos; + + dpoly *poly; + + int bendShift; + + Bit16s *mixBuffers(Bit16s *buf1, Bit16s *buf2, int len); + Bit16s *mixBuffersRingMix(Bit16s *buf1, Bit16s *buf2, int len); + Bit16s *mixBuffersRing(Bit16s *buf1, Bit16s *buf2, int len); + void mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len); + + Bit32s getFiltEnvelope(); + Bit32u getAmpEnvelope(); + Bit32s getPitchEnvelope(); + + void initKeyFollow(int freqNum); + +public: + const PatchCache *patchCache; + EnvelopeStatus envs[3]; + bool play; + + PatchCache cachebackup; + + Partial *pair; + bool alreadyOutputed; + Bit32u age; + + Partial(Synth *synth); + ~Partial(); + + int getOwnerPart() const; + int getKey() const; + const dpoly *getDpoly() const; + bool isActive(); + void activate(int part); + void deactivate(void); + void startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial); + void startDecay(EnvelopeType envnum, Bit32s startval); + void startDecayAll(); + void setBend(float factor); + bool shouldReverb(); + + // Returns true only if data written to buffer + // This function (unlike the one below it) returns processed stereo samples + // made from combining this single partial with its pair, if it has one. + bool produceOutput(Bit16s * partialBuf, long length); + + // This function produces mono sample output using the partial's private internal buffer + Bit16s *generateSamples(long length); +}; + +} + +#endif diff --git a/engines/sci/sfx/softseq/mt32/partialManager.cpp b/engines/sci/sfx/softseq/mt32/partialManager.cpp new file mode 100644 index 0000000000..3d3b6302db --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/partialManager.cpp @@ -0,0 +1,272 @@ +/* Copyright (c) 2003-2005 Various contributors + * + * 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. + */ + +#include + +#include "mt32emu.h" + +using namespace MT32Emu; + +PartialManager::PartialManager(Synth *useSynth) { + this->synth = useSynth; + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) + partialTable[i] = new Partial(synth); +} + +PartialManager::~PartialManager(void) { + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) + delete partialTable[i]; +} + +void PartialManager::getPerPartPartialUsage(int usage[9]) { + memset(usage, 0, 9 * sizeof (int)); + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + if (partialTable[i]->isActive()) + usage[partialTable[i]->getOwnerPart()]++; + } +} + +void PartialManager::clearAlreadyOutputed() { + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) + partialTable[i]->alreadyOutputed = false; +} + +void PartialManager::ageAll() { + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) + partialTable[i]->age++; +} + +bool PartialManager::shouldReverb(int i) { + return partialTable[i]->shouldReverb(); +} + +bool PartialManager::produceOutput(int i, Bit16s *buffer, Bit32u bufferLength) { + return partialTable[i]->produceOutput(buffer, bufferLength); +} + +void PartialManager::deactivateAll() { + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + partialTable[i]->deactivate(); + } +} + +unsigned int PartialManager::setReserve(Bit8u *rset) { + unsigned int pr = 0; + for (int x = 0; x < 9; x++) { + for (int y = 0; y < rset[x]; y++) { + partialReserveTable[pr] = x; + pr++; + } + } + return pr; +} + +Partial *PartialManager::allocPartial(int partNum) { + Partial *outPartial = NULL; + + // Use the first inactive partial reserved for the specified part (if there are any) + // Otherwise, use the last inactive partial, if any + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + if (!partialTable[i]->isActive()) { + outPartial = partialTable[i]; + if (partialReserveTable[i] == partNum) + break; + } + } + if (outPartial != NULL) { + outPartial->activate(partNum); + outPartial->age = 0; + } + return outPartial; +} + +unsigned int PartialManager::getFreePartialCount(void) { + int count = 0; + memset(partialPart, 0, sizeof(partialPart)); + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + if (!partialTable[i]->isActive()) + count++; + else + partialPart[partialTable[i]->getOwnerPart()]++; + } + return count; +} + +/* +bool PartialManager::freePartials(unsigned int needed, int partNum) { + int i; + int myPartPrior = (int)mt32ram.system.reserveSettings[partNum]; + if (myPartPrior 0) { + int selectPart = -1; + //Find the worst offender with more partials than allocated and kill them + most = -1; + mostPart = -1; + int diff; + + for (i=0;i<9;i++) { + diff = partialPart[i] - (int)mt32ram.system.reserveSettings[i]; + + if (diff>0) { + if (diff>most) { + most = diff; + mostPart = i; + } + } + } + selectPart = mostPart; + if (selectPart == -1) { + // All parts are within the allocated limits, you suck + // Look for first partial not of this part that's decaying perhaps? + return false; + } + bool found; + int oldest; + int oldnum; + while (partialPart[selectPart] > (int)mt32ram.system.reserveSettings[selectPart]) { + oldest = -1; + oldnum = -1; + found = false; + for (i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + if (partialTable[i]->isActive) { + if (partialTable[i]->ownerPart == selectPart) { + found = true; + if (partialTable[i]->age > oldest) { + oldest = partialTable[i]->age; + oldnum = i; + } + } + } + } + if (!found) break; + partialTable[oldnum]->deactivate(); + --partialPart[selectPart]; + --needed; + } + + } + return true; + + } else { + //This part has reached its max, must kill off its own + bool found; + int oldest; + int oldnum; + while (needed > 0) { + oldest = -1; + oldnum = -1; + found = false; + for (i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + if (partialTable[i]->isActive) { + if (partialTable[i]->ownerPart == partNum) { + found = true; + if (partialTable[i]->age > oldest) { + oldest = partialTable[i]->age; + oldnum = i; + } + } + } + } + if (!found) break; + partialTable[oldnum]->deactivate(); + --needed; + } + // Couldn't free enough partials, sorry + if (needed>0) return false; + return true; + } + +} +*/ +bool PartialManager::freePartials(unsigned int needed, int partNum) { + if (needed == 0) { + return true; + } + // Reclaim partials reserved for this part + // Kill those that are already decaying first + /* + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + if (partialReserveTable[i] == partNum) { + if (partialTable[i]->ownerPart != partNum) { + if (partialTable[i]->partCache->envs[AMPENV].decaying) { + partialTable[i]->isActive = false; + --needed; + if (needed == 0) + return true; + } + } + } + }*/ + // Then kill those with the lowest part priority -- oldest at the moment + while (needed > 0) { + Bit32u prior = 0; + int priornum = -1; + + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + if (partialReserveTable[i] == partNum && partialTable[i]->isActive() && partialTable[i]->getOwnerPart() != partNum) { + /* + if (mt32ram.system.reserveSettings[partialTable[i]->ownerPart] < prior) { + prior = mt32ram.system.reserveSettings[partialTable[i]->ownerPart]; + priornum = i; + }*/ + if (partialTable[i]->age >= prior) { + prior = partialTable[i]->age; + priornum = i; + } + } + } + if (priornum != -1) { + partialTable[priornum]->deactivate(); + --needed; + } else { + break; + } + } + + // Kill off the oldest partials within this part + while (needed > 0) { + Bit32u oldest = 0; + int oldlist = -1; + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + if (partialTable[i]->getOwnerPart() == partNum && partialTable[i]->isActive()) { + if (partialTable[i]->age >= oldest) { + oldest = partialTable[i]->age; + oldlist = i; + } + } + } + if (oldlist != -1) { + partialTable[oldlist]->deactivate(); + --needed; + } else { + break; + } + } + return needed == 0; +} + +const Partial *PartialManager::getPartial(unsigned int partialNum) const { + if (partialNum > MT32EMU_MAX_PARTIALS - 1) + return NULL; + return partialTable[partialNum]; +} diff --git a/engines/sci/sfx/softseq/mt32/partialManager.h b/engines/sci/sfx/softseq/mt32/partialManager.h new file mode 100644 index 0000000000..b10f93ff02 --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/partialManager.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2003-2005 Various contributors + * + * 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. + */ + +#ifndef MT32EMU_PARTIALMANAGER_H +#define MT32EMU_PARTIALMANAGER_H + +namespace MT32Emu { + +class Synth; + +class PartialManager { +private: + Synth *synth; // Only used for sending debug output + + Partial *partialTable[MT32EMU_MAX_PARTIALS]; + Bit32s partialReserveTable[MT32EMU_MAX_PARTIALS]; + Bit32s partialPart[9]; // The count of partials played per part + +public: + + PartialManager(Synth *synth); + ~PartialManager(); + Partial *allocPartial(int partNum); + unsigned int getFreePartialCount(void); + bool freePartials(unsigned int needed, int partNum); + unsigned int setReserve(Bit8u *rset); + void deactivateAll(); + void ageAll(); + bool produceOutput(int i, Bit16s *buffer, Bit32u bufferLength); + bool shouldReverb(int i); + void clearAlreadyOutputed(); + void getPerPartPartialUsage(int usage[9]); + const Partial *getPartial(unsigned int partialNum) const; +}; + +} + +#endif diff --git a/engines/sci/sfx/softseq/mt32/structures.h b/engines/sci/sfx/softseq/mt32/structures.h new file mode 100644 index 0000000000..ef58c1d20f --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/structures.h @@ -0,0 +1,284 @@ +/* Copyright (c) 2003-2005 Various contributors + * + * 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. + */ + +#ifndef MT32EMU_STRUCTURES_H +#define MT32EMU_STRUCTURES_H + +namespace MT32Emu { + +const unsigned int MAX_SAMPLE_OUTPUT = 4096; + +// MT32EMU_MEMADDR() converts from sysex-padded, MT32EMU_SYSEXMEMADDR converts to it +// Roland provides documentation using the sysex-padded addresses, so we tend to use that in code and output +#define MT32EMU_MEMADDR(x) ((((x) & 0x7f0000) >> 2) | (((x) & 0x7f00) >> 1) | ((x) & 0x7f)) +#define MT32EMU_SYSEXMEMADDR(x) ((((x) & 0x1FC000) << 2) | (((x) & 0x3F80) << 1) | ((x) & 0x7f)) + +#ifdef _MSC_VER +#define MT32EMU_ALIGN_PACKED __declspec(align(1)) +typedef unsigned __int64 Bit64u; +typedef signed __int64 Bit64s; +#else +#define MT32EMU_ALIGN_PACKED __attribute__((packed)) +typedef unsigned long long Bit64u; +typedef signed long long Bit64s; +#endif + +typedef unsigned int Bit32u; +typedef signed int Bit32s; +typedef unsigned short int Bit16u; +typedef signed short int Bit16s; +typedef unsigned char Bit8u; +typedef signed char Bit8s; + +// The following structures represent the MT-32's memory +// Since sysex allows this memory to be written to in blocks of bytes, +// we keep this packed so that we can copy data into the various +// banks directly +#if defined(_MSC_VER) || defined (__MINGW32__) +#pragma pack(push, 1) +#else +#pragma pack(1) +#endif + +struct TimbreParam { + struct commonParam { + char name[10]; + Bit8u pstruct12; // 1&2 0-12 (1-13) + Bit8u pstruct34; // #3&4 0-12 (1-13) + Bit8u pmute; // 0-15 (0000-1111) + Bit8u nosustain; // 0-1(Normal, No sustain) + } MT32EMU_ALIGN_PACKED common; + + struct partialParam { + struct wgParam { + Bit8u coarse; // 0-96 (C1,C#1-C9) + Bit8u fine; // 0-100 (-50 to +50 (cents?)) + Bit8u keyfollow; // 0-16 (-1,-1/2,0,1,1/8,1/4,3/8,1/2,5/8,3/4,7/8,1,5/4,3/2,2.s1,s2) + Bit8u bender; // 0,1 (ON/OFF) + Bit8u waveform; // MT-32: 0-1 (SQU/SAW); LAPC-I: WG WAVEFORM/PCM BANK 0 - 3 (SQU/1, SAW/1, SQU/2, SAW/2) + Bit8u pcmwave; // 0-127 (1-128) + Bit8u pulsewid; // 0-100 + Bit8u pwvelo; // 0-14 (-7 - +7) + } MT32EMU_ALIGN_PACKED wg; + + struct envParam { + Bit8u depth; // 0-10 + Bit8u sensitivity; // 1-100 + Bit8u timekeyfollow; // 0-4 + Bit8u time[4]; // 1-100 + Bit8u level[5]; // 1-100 (-50 - +50) + } MT32EMU_ALIGN_PACKED env; + + struct lfoParam { + Bit8u rate; // 0-100 + Bit8u depth; // 0-100 + Bit8u modsense; // 0-100 + } MT32EMU_ALIGN_PACKED lfo; + + struct tvfParam { + Bit8u cutoff; // 0-100 + Bit8u resonance; // 0-30 + Bit8u keyfollow; // 0-16 (-1,-1/2,1/4,0,1,1/8,1/4,3/8,1/2,5/8,3/2,7/8,1,5/4,3/2,2,s1,s2) + Bit8u biaspoint; // 0-127 (<1A-<7C >1A-7C) + Bit8u biaslevel; // 0-14 (-7 - +7) + Bit8u envdepth; // 0-100 + Bit8u envsense; // 0-100 + Bit8u envdkf; // DEPTH KEY FOLL0W 0-4 + Bit8u envtkf; // TIME KEY FOLLOW 0-4 + Bit8u envtime[5]; // 1-100 + Bit8u envlevel[4]; // 1-100 + } MT32EMU_ALIGN_PACKED tvf; + + struct tvaParam { + Bit8u level; // 0-100 + Bit8u velosens; // 0-100 + Bit8u biaspoint1; // 0-127 (<1A-<7C >1A-7C) + Bit8u biaslevel1; // 0-12 (-12 - 0) + Bit8u biaspoint2; // 0-127 (<1A-<7C >1A-7C) + Bit8u biaslevel2; // 0-12 (-12 - 0) + Bit8u envtkf; // TIME KEY FOLLOW 0-4 + Bit8u envvkf; // VELOS KEY FOLL0W 0-4 + Bit8u envtime[5]; // 1-100 + Bit8u envlevel[4]; // 1-100 + } MT32EMU_ALIGN_PACKED tva; + } MT32EMU_ALIGN_PACKED partial[4]; +} MT32EMU_ALIGN_PACKED; + +struct PatchParam { + Bit8u timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm) + Bit8u timbreNum; // TIMBRE NUMBER 0-63 + Bit8u keyShift; // KEY SHIFT 0-48 (-24 - +24 semitones) + Bit8u fineTune; // FINE TUNE 0-100 (-50 - +50 cents) + Bit8u benderRange; // BENDER RANGE 0-24 + Bit8u assignMode; // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4) + Bit8u reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON) + Bit8u dummy; // (DUMMY) +} MT32EMU_ALIGN_PACKED; + +struct MemParams { + // NOTE: The MT-32 documentation only specifies PatchTemp areas for parts 1-8. + // The LAPC-I documentation specified an additional area for rhythm at the end, + // where all parameters but fine tune, assign mode and output level are ignored + struct PatchTemp { + PatchParam patch; + Bit8u outlevel; // OUTPUT LEVEL 0-100 + Bit8u panpot; // PANPOT 0-14 (R-L) + Bit8u dummyv[6]; + } MT32EMU_ALIGN_PACKED; + + PatchTemp patchSettings[9]; + + struct RhythmTemp { + Bit8u timbre; // TIMBRE 0-94 (M1-M64,R1-30,OFF) + Bit8u outlevel; // OUTPUT LEVEL 0-100 + Bit8u panpot; // PANPOT 0-14 (R-L) + Bit8u reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON) + } MT32EMU_ALIGN_PACKED; + + RhythmTemp rhythmSettings[85]; + + TimbreParam timbreSettings[8]; + + PatchParam patches[128]; + + // NOTE: There are only 30 timbres in the "rhythm" bank for MT-32; the additional 34 are for LAPC-I and above + struct PaddedTimbre { + TimbreParam timbre; + Bit8u padding[10]; + } MT32EMU_ALIGN_PACKED; + + PaddedTimbre timbres[64 + 64 + 64 + 64]; // Group A, Group B, Memory, Rhythm + + struct SystemArea { + Bit8u masterTune; // MASTER TUNE 0-127 432.1-457.6Hz + Bit8u reverbMode; // REVERB MODE 0-3 (room, hall, plate, tap delay) + Bit8u reverbTime; // REVERB TIME 0-7 (1-8) + Bit8u reverbLevel; // REVERB LEVEL 0-7 (1-8) + Bit8u reserveSettings[9]; // PARTIAL RESERVE (PART 1) 0-32 + Bit8u chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF) + Bit8u masterVol; // MASTER VOLUME 0-100 + } MT32EMU_ALIGN_PACKED; + + SystemArea system; +}; + +#if defined(_MSC_VER) || defined (__MINGW32__) +#pragma pack(pop) +#else +#pragma pack() +#endif + +struct PCMWaveEntry { + Bit32u addr; + Bit32u len; + double tune; + bool loop; +}; + +struct soundaddr { + Bit16u pcmplace; + Bit16u pcmoffset; +}; + +struct StereoVolume { + Bit16s leftvol; + Bit16s rightvol; +}; + +// This is basically a per-partial, pre-processed combination of timbre and patch/rhythm settings +struct PatchCache { + bool playPartial; + bool PCMPartial; + int pcm; + char waveform; + int pulsewidth; + int pwsens; + + float pitch; + + int lfodepth; + int lforate; + Bit32u lfoperiod; + int modsense; + + float pitchKeyfollow; + + int filtkeyfollow; + + int tvfbias; + int tvfblevel; + int tvfdir; + + int ampbias[2]; + int ampblevel[2]; + int ampdir[2]; + + int ampdepth; + int amplevel; + + bool useBender; + float benderRange; // 0.0, 1.0, .., 24.0 (semitones) + + TimbreParam::partialParam::envParam pitchEnv; + TimbreParam::partialParam::tvaParam ampEnv; + TimbreParam::partialParam::tvfParam filtEnv; + + Bit32s pitchsustain; + Bit32s filtsustain; + + Bit32u structureMix; + int structurePosition; + int structurePair; + + // The following fields are actually common to all partials in the timbre + bool dirty; + Bit32u partialCount; + bool sustain; + float pitchShift; + bool reverb; + const StereoVolume *pansetptr; +}; + +class Partial; // Forward reference for class defined in partial.h + +struct dpoly { + bool isPlaying; + + unsigned int key; + int freqnum; + int vel; + + bool isDecay; + + const Bit32u *volumeptr; + + Partial *partials[4]; + + bool pedalhold; // This marks keys that have been released on the keyboard, but are being held by the pedal + bool sustain; + + bool isActive() const; + Bit32u getAge() const; +}; + +} + +#endif diff --git a/engines/sci/sfx/softseq/mt32/synth.cpp b/engines/sci/sfx/softseq/mt32/synth.cpp new file mode 100644 index 0000000000..5ae196dc63 --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/synth.cpp @@ -0,0 +1,1199 @@ +/* Copyright (c) 2003-2005 Various contributors + * + * 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. + */ + +#include +#include +#include + +#include "mt32emu.h" + +#ifdef MACOSX +// Older versions of Mac OS X didn't supply a powf function. To ensure +// binary compatibility, we force using pow instead of powf (the only +// potential drawback is that it might be a little bit slower). +#define powf pow +#endif + +namespace MT32Emu { + +const int MAX_SYSEX_SIZE = 512; + +const ControlROMMap ControlROMMaps[5] = { + // ID IDc IDbytes PCMmap PCMc tmbrA tmbrAO, tmbrB tmbrBO, tmbrR trC rhythm rhyC rsrv panpot prog + {0x4014, 22, "\000 ver1.04 14 July 87 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57D0, 0x57E2}, // MT-32 revision 0 + {0x4014, 22, "\000 ver1.06 31 Aug, 87 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x7414, 85, 0x57D9, 0x57E2, 0x57F4}, // MT-32 revision 0 + {0x4010, 22, "\000 ver1.07 10 Oct, 87 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x73fe, 85, 0x57B1, 0x57BA, 0x57CC}, // MT-32 revision 1 + {0x4010, 22, "\000verX.XX 30 Sep, 88 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x741C, 85, 0x57E5, 0x57EE, 0x5800}, // MT-32 Blue Ridge mod + {0x2205, 22, "\000CM32/LAPC1.02 891205", 0x8100, 256, 0x8000, 0x8000, 0x8080, 0x8000, 0x8500, 64, 0x8580, 85, 0x4F93, 0x4F9C, 0x4FAE} // CM-32L + // (Note that all but CM-32L ROM actually have 86 entries for rhythmTemp) +}; + +float iir_filter_normal(float input, float *hist1_ptr, float *coef_ptr) { + float *hist2_ptr; + float output,new_hist; + + hist2_ptr = hist1_ptr + 1; // next history + + // 1st number of coefficients array is overall input scale factor, or filter gain + output = input * (*coef_ptr++); + + output = output - *hist1_ptr * (*coef_ptr++); + new_hist = output - *hist2_ptr * (*coef_ptr++); // poles + + output = new_hist + *hist1_ptr * (*coef_ptr++); + output = output + *hist2_ptr * (*coef_ptr++); // zeros + + *hist2_ptr++ = *hist1_ptr; + *hist1_ptr++ = new_hist; + hist1_ptr++; + hist2_ptr++; + + // i = 1 + output = output - *hist1_ptr * (*coef_ptr++); + new_hist = output - *hist2_ptr * (*coef_ptr++); // poles + + output = new_hist + *hist1_ptr * (*coef_ptr++); + output = output + *hist2_ptr * (*coef_ptr++); // zeros + + *hist2_ptr++ = *hist1_ptr; + *hist1_ptr++ = new_hist; + + return(output); +} + +Bit8u Synth::calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum) { + for (unsigned int i = 0; i < len; i++) { + checksum = checksum + data[i]; + } + checksum = checksum & 0x7f; + if (checksum) + checksum = 0x80 - checksum; + return checksum; +} + +Synth::Synth() { + isOpen = false; + reverbModel = NULL; + partialManager = NULL; + memset(parts, 0, sizeof(parts)); +} + +Synth::~Synth() { + close(); // Make sure we're closed and everything is freed +} + +int Synth::report(ReportType type, const void *data) { + if (myProp.report != NULL) { + return myProp.report(myProp.userData, type, data); + } + return 0; +} + +void Synth::printDebug(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + if (myProp.printDebug != NULL) { + myProp.printDebug(myProp.userData, fmt, ap); + } else { + vprintf(fmt, ap); + printf("\n"); + } + va_end(ap); +} + +void Synth::initReverb(Bit8u newRevMode, Bit8u newRevTime, Bit8u newRevLevel) { + // FIXME:KG: I don't think it's necessary to recreate the reverbModel... Just set the parameters + if (reverbModel != NULL) + delete reverbModel; + reverbModel = new revmodel(); + + switch (newRevMode) { + case 0: + reverbModel->setroomsize(.1f); + reverbModel->setdamp(.75f); + break; + case 1: + reverbModel->setroomsize(.5f); + reverbModel->setdamp(.5f); + break; + case 2: + reverbModel->setroomsize(.5f); + reverbModel->setdamp(.1f); + break; + case 3: + reverbModel->setroomsize(1.0f); + reverbModel->setdamp(.75f); + break; + default: + reverbModel->setroomsize(.1f); + reverbModel->setdamp(.5f); + break; + } + reverbModel->setdry(1); + reverbModel->setwet((float)newRevLevel / 8.0f); + reverbModel->setwidth((float)newRevTime / 8.0f); +} + +File *Synth::openFile(const char *filename, File::OpenMode mode) { + if (myProp.openFile != NULL) { + return myProp.openFile(myProp.userData, filename, mode); + } + char pathBuf[2048]; + if (myProp.baseDir != NULL) { + strcpy(&pathBuf[0], myProp.baseDir); + strcat(&pathBuf[0], filename); + filename = pathBuf; + } + ANSIFile *file = new ANSIFile(); + if (!file->open(filename, mode)) { + delete file; + return NULL; + } + return file; +} + +void Synth::closeFile(File *file) { + if (myProp.closeFile != NULL) { + myProp.closeFile(myProp.userData, file); + } else { + file->close(); + delete file; + } +} + +bool Synth::loadPreset(File *file) { + bool inSys = false; + Bit8u sysexBuf[MAX_SYSEX_SIZE]; + Bit16u syslen = 0; + bool rc = true; + for (;;) { + Bit8u c; + if (!file->readBit8u(&c)) { + if (!file->isEOF()) { + rc = false; + } + break; + } + sysexBuf[syslen] = c; + if (inSys) { + syslen++; + if (c == 0xF7) { + playSysex(&sysexBuf[0], syslen); + inSys = false; + syslen = 0; + } else if (syslen == MAX_SYSEX_SIZE) { + printDebug("MAX_SYSEX_SIZE (%d) exceeded while processing preset, ignoring message", MAX_SYSEX_SIZE); + inSys = false; + syslen = 0; + } + } else if (c == 0xF0) { + syslen++; + inSys = true; + } + } + return rc; +} + +bool Synth::loadControlROM(const char *filename) { + File *file = openFile(filename, File::OpenMode_read); // ROM File + if (file == NULL) { + return false; + } + bool rc = (file->read(controlROMData, CONTROL_ROM_SIZE) == CONTROL_ROM_SIZE); + + closeFile(file); + if (!rc) + return rc; + + // Control ROM successfully loaded, now check whether it's a known type + controlROMMap = NULL; + for (unsigned int i = 0; i < sizeof (ControlROMMaps) / sizeof (ControlROMMaps[0]); i++) { + if (memcmp(&controlROMData[ControlROMMaps[i].idPos], ControlROMMaps[i].idBytes, ControlROMMaps[i].idLen) == 0) { + controlROMMap = &ControlROMMaps[i]; + return true; + } + } + return false; +} + +bool Synth::loadPCMROM(const char *filename) { + File *file = openFile(filename, File::OpenMode_read); // ROM File + if (file == NULL) { + return false; + } + bool rc = true; + int i; + for (i = 0; i < pcmROMSize; i++) { + Bit8u s; + if (!file->readBit8u(&s)) { + if (!file->isEOF()) { + rc = false; + } + break; + } + Bit8u c; + if (!file->readBit8u(&c)) { + if (!file->isEOF()) { + rc = false; + } else { + printDebug("PCM ROM file has an odd number of bytes! Ignoring last"); + } + break; + } + + short e; + int bit; + int u; + int order[16] = {0, 9, 1 ,2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8}; + + e = 0; + for (u = 0; u < 15; u++) { + if (order[u] < 8) + bit = (s >> (7 - order[u])) & 0x1; + else + bit = (c >> (7 - (order[u] - 8))) & 0x1; + e = e | (short)(bit << (15 - u)); + } + + /* + //Bit16s e = ( ((s & 0x7f) << 4) | ((c & 0x40) << 6) | ((s & 0x80) << 6) | ((c & 0x3f))) << 2; + if (e<0) + e = -32767 - e; + int ut = abs(e); + int dif = 0x7fff - ut; + x = exp(((float)((float)0x8000-(float)dif) / (float)0x1000)); + e = (int)((float)e * (x/3200)); + */ + + // File is companded (dB?), convert to linear PCM + // MINDB = -96 + // MAXDB = -15 + float testval; + testval = (float)((~e) & 0x7fff); + testval = -(testval / 400.00f); + //testval = -(testval / 341.32291666666666666666666666667); + float vol = powf(8, testval / 20) * 32767.0f; + + if (e > 0) + vol = -vol; + + pcmROMData[i] = (Bit16s)vol; + } + if (i != pcmROMSize) { + printDebug("PCM ROM file is too short (expected %d, got %d)", pcmROMSize, i); + rc = false; + } + closeFile(file); + return rc; +} + +bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) { + ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[mapAddress]; + for (int i = 0; i < count; i++) { + int rAddr = tps[i].pos * 0x800; + int rLenExp = (tps[i].len & 0x70) >> 4; + int rLen = 0x800 << rLenExp; + bool rLoop = (tps[i].len & 0x80) != 0; + //Bit8u rFlag = tps[i].len & 0x0F; + Bit16u rTuneOffset = (tps[i].pitchMSB << 8) | tps[i].pitchLSB; + // The number below is confirmed to a reasonable degree of accuracy on CM-32L + double STANDARDFREQ = 442.0; + float rTune = (float)(STANDARDFREQ * pow(2.0, (0x5000 - rTuneOffset) / 4056.0 - 9.0 / 12.0)); + //printDebug("%f,%d,%d", pTune, tps[i].pitchCoarse, tps[i].pitchFine); + if (rAddr + rLen > pcmROMSize) { + printDebug("Control ROM error: Wave map entry %d points to invalid PCM address 0x%04X, length 0x%04X", i, rAddr, rLen); + return false; + } + pcmWaves[i].addr = rAddr; + pcmWaves[i].len = rLen; + pcmWaves[i].loop = rLoop; + pcmWaves[i].tune = rTune; + } + return false; +} + +bool Synth::initRhythmTimbre(int timbreNum, const Bit8u *mem, unsigned int memLen) { + if (memLen < sizeof(TimbreParam::commonParam)) { + return false; + } + TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre; + memcpy(&timbre->common, mem, 14); + unsigned int memPos = 14; + char drumname[11]; + strncpy(drumname, timbre->common.name, 10); + drumname[10] = 0; + for (int t = 0; t < 4; t++) { + if (((timbre->common.pmute >> t) & 0x1) == 0x1) { + if (memPos + 58 >= memLen) { + return false; + } + memcpy(&timbre->partial[t], mem + memPos, 58); + memPos += 58; + } + } + return true; +} + +bool Synth::initRhythmTimbres(Bit16u mapAddress, Bit16u count) { + const Bit8u *drumMap = &controlROMData[mapAddress]; + int timbreNum = 192; + for (Bit16u i = 0; i < count * 2; i += 2) { + Bit16u address = (drumMap[i + 1] << 8) | drumMap[i]; + /* + // This check is nonsensical when the control ROM is the full 64KB addressable by 16-bit absolute pointers (which it is) + if (address >= CONTROL_ROM_SIZE) { + printDebug("Control ROM error: Timbre map entry 0x%04x points to invalid timbre address 0x%04x", i, address); + return false; + } + */ + if (!initRhythmTimbre(timbreNum++, &controlROMData[address], CONTROL_ROM_SIZE - address)) { + printDebug("Control ROM error: Timbre map entry 0x%04x points to invalid timbre 0x%04x", i, address); + return false; + } + } + return true; +} + +bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int startTimbre) { + for (Bit16u i = mapAddress; i < mapAddress + 0x80; i += 2) { + Bit16u address = (controlROMData[i + 1] << 8) | controlROMData[i]; + if (address + sizeof(TimbreParam) > CONTROL_ROM_SIZE) { + printDebug("Control ROM error: Timbre map entry 0x%04x points to invalid timbre address 0x%04x", i, address); + return false; + } + address = address + offset; + TimbreParam *timbre = &mt32ram.timbres[startTimbre++].timbre; + memcpy(timbre, &controlROMData[address], sizeof(TimbreParam)); + } + return true; +} + +bool Synth::open(SynthProperties &useProp) { + if (isOpen) + return false; + + myProp = useProp; + if (useProp.baseDir != NULL) { + myProp.baseDir = new char[strlen(useProp.baseDir) + 1]; + strcpy(myProp.baseDir, useProp.baseDir); + } + + // This is to help detect bugs + memset(&mt32ram, '?', sizeof(mt32ram)); + + printDebug("Loading Control ROM"); + if (!loadControlROM("CM32L_CONTROL.ROM")) { + if (!loadControlROM("MT32_CONTROL.ROM")) { + printDebug("Init Error - Missing or invalid MT32_CONTROL.ROM"); + report(ReportType_errorControlROM, NULL); + return false; + } + } + + // 512KB PCM ROM for MT-32, etc. + // 1MB PCM ROM for CM-32L, LAPC-I, CM-64, CM-500 + // Note that the size below is given in samples (16-bit), not bytes + pcmROMSize = controlROMMap->pcmCount == 256 ? 512 * 1024 : 256 * 1024; + pcmROMData = new Bit16s[pcmROMSize]; + + printDebug("Loading PCM ROM"); + if (!loadPCMROM("CM32L_PCM.ROM")) { + if (!loadPCMROM("MT32_PCM.ROM")) { + printDebug("Init Error - Missing MT32_PCM.ROM"); + report(ReportType_errorPCMROM, NULL); + return false; + } + } + + printDebug("Initialising Timbre Bank A"); + if (!initTimbres(controlROMMap->timbreAMap, controlROMMap->timbreAOffset, 0)) { + return false; + } + + printDebug("Initialising Timbre Bank B"); + if (!initTimbres(controlROMMap->timbreBMap, controlROMMap->timbreBOffset, 64)) { + return false; + } + + printDebug("Initialising Timbre Bank R"); + if (!initRhythmTimbres(controlROMMap->timbreRMap, controlROMMap->timbreRCount)) { + return false; + } + + printDebug("Initialising Timbre Bank M"); + // CM-64 seems to initialise all bytes in this bank to 0. + memset(&mt32ram.timbres[128], 0, sizeof (mt32ram.timbres[128]) * 64); + + partialManager = new PartialManager(this); + + pcmWaves = new PCMWaveEntry[controlROMMap->pcmCount]; + + printDebug("Initialising PCM List"); + initPCMList(controlROMMap->pcmTable, controlROMMap->pcmCount); + + printDebug("Initialising Rhythm Temp"); + memcpy(mt32ram.rhythmSettings, &controlROMData[controlROMMap->rhythmSettings], controlROMMap->rhythmSettingsCount * 4); + + printDebug("Initialising Patches"); + for (Bit8u i = 0; i < 128; i++) { + PatchParam *patch = &mt32ram.patches[i]; + patch->timbreGroup = i / 64; + patch->timbreNum = i % 64; + patch->keyShift = 24; + patch->fineTune = 50; + patch->benderRange = 12; + patch->assignMode = 0; + patch->reverbSwitch = 1; + patch->dummy = 0; + } + + printDebug("Initialising System"); + // The MT-32 manual claims that "Standard pitch" is 442Hz. + mt32ram.system.masterTune = 0x4A; // Confirmed on CM-64 + mt32ram.system.reverbMode = 0; // Confirmed + mt32ram.system.reverbTime = 5; // Confirmed + mt32ram.system.reverbLevel = 3; // Confirmed + memcpy(mt32ram.system.reserveSettings, &controlROMData[controlROMMap->reserveSettings], 9); // Confirmed + for (Bit8u i = 0; i < 9; i++) { + // This is the default: {1, 2, 3, 4, 5, 6, 7, 8, 9} + // An alternative configuration can be selected by holding "Master Volume" + // and pressing "PART button 1" on the real MT-32's frontpanel. + // The channel assignment is then {0, 1, 2, 3, 4, 5, 6, 7, 9} + mt32ram.system.chanAssign[i] = i + 1; + } + mt32ram.system.masterVol = 100; // Confirmed + if (!refreshSystem()) + return false; + + for (int i = 0; i < 8; i++) { + mt32ram.patchSettings[i].outlevel = 80; + mt32ram.patchSettings[i].panpot = controlROMData[controlROMMap->panSettings + i]; + memset(mt32ram.patchSettings[i].dummyv, 0, sizeof(mt32ram.patchSettings[i].dummyv)); + parts[i] = new Part(this, i); + parts[i]->setProgram(controlROMData[controlROMMap->programSettings + i]); + } + parts[8] = new RhythmPart(this, 8); + + // For resetting mt32 mid-execution + mt32default = mt32ram; + + iirFilter = &iir_filter_normal; + +#ifdef MT32EMU_HAVE_X86 + bool availableSSE = DetectSIMD(); + bool available3DNow = Detect3DNow(); + + if (availableSSE) + report(ReportType_availableSSE, NULL); + if (available3DNow) + report(ReportType_available3DNow, NULL); + + if (available3DNow) { + printDebug("Detected and using SIMD (AMD 3DNow) extensions"); + iirFilter = &iir_filter_3dnow; + report(ReportType_using3DNow, NULL); + } else if (availableSSE) { + printDebug("Detected and using SIMD (Intel SSE) extensions"); + iirFilter = &iir_filter_sse; + report(ReportType_usingSSE, NULL); + } +#endif + + isOpen = true; + isEnabled = false; + + printDebug("*** Initialisation complete ***"); + return true; +} + +void Synth::close(void) { + if (!isOpen) + return; + + tables.freeNotes(); + if (partialManager != NULL) { + delete partialManager; + partialManager = NULL; + } + + if (reverbModel != NULL) { + delete reverbModel; + reverbModel = NULL; + } + + for (int i = 0; i < 9; i++) { + if (parts[i] != NULL) { + delete parts[i]; + parts[i] = NULL; + } + } + if (myProp.baseDir != NULL) { + delete myProp.baseDir; + myProp.baseDir = NULL; + } + + delete[] pcmWaves; + delete[] pcmROMData; + isOpen = false; +} + +void Synth::playMsg(Bit32u msg) { + // FIXME: Implement active sensing + unsigned char code = (unsigned char)((msg & 0x0000F0) >> 4); + unsigned char chan = (unsigned char) (msg & 0x00000F); + unsigned char note = (unsigned char)((msg & 0x00FF00) >> 8); + unsigned char velocity = (unsigned char)((msg & 0xFF0000) >> 16); + isEnabled = true; + + //printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note); + + char part = chantable[chan]; + if (part < 0 || part > 8) { + printDebug("Play msg on unreg chan %d (%d): code=0x%01x, vel=%d", chan, part, code, velocity); + return; + } + playMsgOnPart(part, code, note, velocity); +} + +void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity) { + Bit32u bend; + + //printDebug("Synth::playMsg(0x%02x)",msg); + switch (code) { + case 0x8: + //printDebug("Note OFF - Part %d", part); + // The MT-32 ignores velocity for note off + parts[part]->stopNote(note); + break; + case 0x9: + //printDebug("Note ON - Part %d, Note %d Vel %d", part, note, velocity); + if (velocity == 0) { + // MIDI defines note-on with velocity 0 as being the same as note-off with velocity 40 + parts[part]->stopNote(note); + } else { + parts[part]->playNote(note, velocity); + } + break; + case 0xB: // Control change + switch (note) { + case 0x01: // Modulation + //printDebug("Modulation: %d", velocity); + parts[part]->setModulation(velocity); + break; + case 0x07: // Set volume + //printDebug("Volume set: %d", velocity); + parts[part]->setVolume(velocity); + break; + case 0x0A: // Pan + //printDebug("Pan set: %d", velocity); + parts[part]->setPan(velocity); + break; + case 0x0B: + //printDebug("Expression set: %d", velocity); + parts[part]->setExpression(velocity); + break; + case 0x40: // Hold (sustain) pedal + //printDebug("Hold pedal set: %d", velocity); + parts[part]->setHoldPedal(velocity>=64); + break; + + case 0x79: // Reset all controllers + //printDebug("Reset all controllers"); + //FIXME: Check for accuracy against real thing + parts[part]->setVolume(100); + parts[part]->setExpression(127); + parts[part]->setPan(64); + parts[part]->setBend(0x2000); + parts[part]->setHoldPedal(false); + break; + + case 0x7B: // All notes off + //printDebug("All notes off"); + parts[part]->allNotesOff(); + break; + + default: + printDebug("Unknown MIDI Control code: 0x%02x - vel 0x%02x", note, velocity); + break; + } + + break; + case 0xC: // Program change + //printDebug("Program change %01x", note); + parts[part]->setProgram(note); + break; + case 0xE: // Pitch bender + bend = (velocity << 7) | (note); + //printDebug("Pitch bender %02x", bend); + parts[part]->setBend(bend); + break; + default: + printDebug("Unknown Midi code: 0x%01x - %02x - %02x", code, note, velocity); + break; + } + + //midiOutShortMsg(m_out, msg); +} + +void Synth::playSysex(const Bit8u *sysex, Bit32u len) { + if (len < 2) { + printDebug("playSysex: Message is too short for sysex (%d bytes)", len); + } + if (sysex[0] != 0xF0) { + printDebug("playSysex: Message lacks start-of-sysex (0xF0)"); + return; + } + // Due to some programs (e.g. Java) sending buffers with junk at the end, we have to go through and find the end marker rather than relying on len. + Bit32u endPos; + for (endPos = 1; endPos < len; endPos++) + { + if (sysex[endPos] == 0xF7) + break; + } + if (endPos == len) { + printDebug("playSysex: Message lacks end-of-sysex (0xf7)"); + return; + } + playSysexWithoutFraming(sysex + 1, endPos - 1); +} + +void Synth::playSysexWithoutFraming(const Bit8u *sysex, Bit32u len) { + if (len < 4) { + printDebug("playSysexWithoutFraming: Message is too short (%d bytes)!", len); + return; + } + if (sysex[0] != SYSEX_MANUFACTURER_ROLAND) { + printDebug("playSysexWithoutFraming: Header not intended for this device manufacturer: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]); + return; + } + if (sysex[2] == SYSEX_MDL_D50) { + printDebug("playSysexWithoutFraming: Header is intended for model D-50 (not yet supported): %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]); + return; + } + else if (sysex[2] != SYSEX_MDL_MT32) { + printDebug("playSysexWithoutFraming: Header not intended for model MT-32: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]); + return; + } + playSysexWithoutHeader(sysex[1], sysex[3], sysex + 4, len - 4); +} + +void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len) { + if (device > 0x10) { + // We have device ID 0x10 (default, but changeable, on real MT-32), < 0x10 is for channels + printDebug("playSysexWithoutHeader: Message is not intended for this device ID (provided: %02x, expected: 0x10 or channel)", (int)device); + return; + } + if (len < 4) { + printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len); + return; + } + unsigned char checksum = calcSysexChecksum(sysex, len - 1, 0); + if (checksum != sysex[len - 1]) { + printDebug("playSysexWithoutHeader: Message checksum is incorrect (provided: %02x, expected: %02x)!", sysex[len - 1], checksum); + return; + } + len -= 1; // Exclude checksum + switch (command) { + case SYSEX_CMD_DT1: + writeSysex(device, sysex, len); + break; + case SYSEX_CMD_RQ1: + readSysex(device, sysex, len); + break; + default: + printDebug("playSysexWithoutFraming: Unsupported command %02x", command); + return; + } +} + +void Synth::readSysex(unsigned char /*device*/, const Bit8u * /*sysex*/, Bit32u /*len*/) { +} + +const MemoryRegion memoryRegions[8] = { + {MR_PatchTemp, MT32EMU_MEMADDR(0x030000), sizeof(MemParams::PatchTemp), 9}, + {MR_RhythmTemp, MT32EMU_MEMADDR(0x030110), sizeof(MemParams::RhythmTemp), 85}, + {MR_TimbreTemp, MT32EMU_MEMADDR(0x040000), sizeof(TimbreParam), 8}, + {MR_Patches, MT32EMU_MEMADDR(0x050000), sizeof(PatchParam), 128}, + {MR_Timbres, MT32EMU_MEMADDR(0x080000), sizeof(MemParams::PaddedTimbre), 64 + 64 + 64 + 64}, + {MR_System, MT32EMU_MEMADDR(0x100000), sizeof(MemParams::SystemArea), 1}, + {MR_Display, MT32EMU_MEMADDR(0x200000), MAX_SYSEX_SIZE - 1, 1}, + {MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1} +}; + +const int NUM_REGIONS = sizeof(memoryRegions) / sizeof(MemoryRegion); + +void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) { + Bit32u addr = (sysex[0] << 16) | (sysex[1] << 8) | (sysex[2]); + addr = MT32EMU_MEMADDR(addr); + sysex += 3; + len -= 3; + //printDebug("Sysex addr: 0x%06x", MT32EMU_SYSEXMEMADDR(addr)); + // NOTE: Please keep both lower and upper bounds in each check, for ease of reading + + // Process channel-specific sysex by converting it to device-global + if (device < 0x10) { + printDebug("WRITE-CHANNEL: Channel %d temp area 0x%06x", device, MT32EMU_SYSEXMEMADDR(addr)); + if (/*addr >= MT32EMU_MEMADDR(0x000000) && */addr < MT32EMU_MEMADDR(0x010000)) { + int offset; + if (chantable[device] == -1) { + printDebug(" (Channel not mapped to a partial... 0 offset)"); + offset = 0; + } else if (chantable[device] == 8) { + printDebug(" (Channel mapped to rhythm... 0 offset)"); + offset = 0; + } else { + offset = chantable[device] * sizeof(MemParams::PatchTemp); + printDebug(" (Setting extra offset to %d)", offset); + } + addr += MT32EMU_MEMADDR(0x030000) + offset; + } else if (/*addr >= 0x010000 && */ addr < MT32EMU_MEMADDR(0x020000)) { + addr += MT32EMU_MEMADDR(0x030110) - MT32EMU_MEMADDR(0x010000); + } else if (/*addr >= 0x020000 && */ addr < MT32EMU_MEMADDR(0x030000)) { + int offset; + if (chantable[device] == -1) { + printDebug(" (Channel not mapped to a partial... 0 offset)"); + offset = 0; + } else if (chantable[device] == 8) { + printDebug(" (Channel mapped to rhythm... 0 offset)"); + offset = 0; + } else { + offset = chantable[device] * sizeof(TimbreParam); + printDebug(" (Setting extra offset to %d)", offset); + } + addr += MT32EMU_MEMADDR(0x040000) - MT32EMU_MEMADDR(0x020000) + offset; + } else { + printDebug("PlaySysexWithoutHeader: Invalid channel %d address 0x%06x", device, MT32EMU_SYSEXMEMADDR(addr)); + return; + } + } + + // Process device-global sysex (possibly converted from channel-specific sysex above) + for (;;) { + // Find the appropriate memory region + int regionNum; + const MemoryRegion *region = NULL; // Initialised to please compiler + for (regionNum = 0; regionNum < NUM_REGIONS; regionNum++) { + region = &memoryRegions[regionNum]; + if (region->contains(addr)) { + writeMemoryRegion(region, addr, region->getClampedLen(addr, len), sysex); + break; + } + } + if (regionNum == NUM_REGIONS) { + printDebug("Sysex write to unrecognised address %06x, len %d", MT32EMU_SYSEXMEMADDR(addr), len); + break; + } + Bit32u next = region->next(addr, len); + if (next == 0) { + break; + } + addr += next; + sysex += next; + len -= next; + } +} + +void Synth::readMemory(Bit32u addr, Bit32u len, Bit8u *data) { + int regionNum; + const MemoryRegion *region = NULL; + for (regionNum = 0; regionNum < NUM_REGIONS; regionNum++) { + region = &memoryRegions[regionNum]; + if (region->contains(addr)) { + readMemoryRegion(region, addr, len, data); + break; + } + } +} + +void Synth::readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data) { + unsigned int first = region->firstTouched(addr); + //unsigned int last = region->lastTouched(addr, len); + unsigned int off = region->firstTouchedOffset(addr); + len = region->getClampedLen(addr, len); + + unsigned int m; + + switch(region->type) { + case MR_PatchTemp: + for (m = 0; m < len; m++) + data[m] = ((Bit8u *)&mt32ram.patchSettings[first])[off + m]; + break; + case MR_RhythmTemp: + for (m = 0; m < len; m++) + data[m] = ((Bit8u *)&mt32ram.rhythmSettings[first])[off + m]; + break; + case MR_TimbreTemp: + for (m = 0; m < len; m++) + data[m] = ((Bit8u *)&mt32ram.timbreSettings[first])[off + m]; + break; + case MR_Patches: + for (m = 0; m < len; m++) + data[m] = ((Bit8u *)&mt32ram.patches[first])[off + m]; + break; + case MR_Timbres: + for (m = 0; m < len; m++) + data[m] = ((Bit8u *)&mt32ram.timbres[first])[off + m]; + break; + case MR_System: + for (m = 0; m < len; m++) + data[m] = ((Bit8u *)&mt32ram.system)[m + off]; + break; + default: + for (m = 0; m < len; m += 2) { + data[m] = 0xff; + if (m + 1 < len) { + data[m+1] = (Bit8u)region->type; + } + } + // TODO: Don't care about the others ATM + break; + } + +} + +void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data) { + unsigned int first = region->firstTouched(addr); + unsigned int last = region->lastTouched(addr, len); + unsigned int off = region->firstTouchedOffset(addr); + switch (region->type) { + case MR_PatchTemp: + for (unsigned int m = 0; m < len; m++) { + ((Bit8u *)&mt32ram.patchSettings[first])[off + m] = data[m]; + } + //printDebug("Patch temp: Patch %d, offset %x, len %d", off/16, off % 16, len); + + for (unsigned int i = first; i <= last; i++) { + int absTimbreNum = mt32ram.patchSettings[i].patch.timbreGroup * 64 + mt32ram.patchSettings[i].patch.timbreNum; + char timbreName[11]; + memcpy(timbreName, mt32ram.timbres[absTimbreNum].timbre.common.name, 10); + timbreName[10] = 0; + printDebug("WRITE-PARTPATCH (%d-%d@%d..%d): %d; timbre=%d (%s), outlevel=%d", first, last, off, off + len, i, absTimbreNum, timbreName, mt32ram.patchSettings[i].outlevel); + if (parts[i] != NULL) { + if (i != 8) { + // Note: Confirmed on CM-64 that we definitely *should* update the timbre here, + // but only in the case that the sysex actually writes to those values + if (i == first && off > 2) { + printDebug(" (Not updating timbre, since those values weren't touched)"); + } else { + parts[i]->setTimbre(&mt32ram.timbres[parts[i]->getAbsTimbreNum()].timbre); + } + } + parts[i]->refresh(); + } + } + break; + case MR_RhythmTemp: + for (unsigned int m = 0; m < len; m++) + ((Bit8u *)&mt32ram.rhythmSettings[first])[off + m] = data[m]; + for (unsigned int i = first; i <= last; i++) { + int timbreNum = mt32ram.rhythmSettings[i].timbre; + char timbreName[11]; + if (timbreNum < 94) { + memcpy(timbreName, mt32ram.timbres[128 + timbreNum].timbre.common.name, 10); + timbreName[10] = 0; + } else { + strcpy(timbreName, "[None]"); + } + printDebug("WRITE-RHYTHM (%d-%d@%d..%d): %d; level=%02x, panpot=%02x, reverb=%02x, timbre=%d (%s)", first, last, off, off + len, i, mt32ram.rhythmSettings[i].outlevel, mt32ram.rhythmSettings[i].panpot, mt32ram.rhythmSettings[i].reverbSwitch, mt32ram.rhythmSettings[i].timbre, timbreName); + } + if (parts[8] != NULL) { + parts[8]->refresh(); + } + break; + case MR_TimbreTemp: + for (unsigned int m = 0; m < len; m++) + ((Bit8u *)&mt32ram.timbreSettings[first])[off + m] = data[m]; + for (unsigned int i = first; i <= last; i++) { + char instrumentName[11]; + memcpy(instrumentName, mt32ram.timbreSettings[i].common.name, 10); + instrumentName[10] = 0; + printDebug("WRITE-PARTTIMBRE (%d-%d@%d..%d): timbre=%d (%s)", first, last, off, off + len, i, instrumentName); + if (parts[i] != NULL) { + parts[i]->refresh(); + } + } + break; + case MR_Patches: + for (unsigned int m = 0; m < len; m++) + ((Bit8u *)&mt32ram.patches[first])[off + m] = data[m]; + for (unsigned int i = first; i <= last; i++) { + PatchParam *patch = &mt32ram.patches[i]; + int patchAbsTimbreNum = patch->timbreGroup * 64 + patch->timbreNum; + char instrumentName[11]; + memcpy(instrumentName, mt32ram.timbres[patchAbsTimbreNum].timbre.common.name, 10); + instrumentName[10] = 0; + Bit8u *n = (Bit8u *)patch; + printDebug("WRITE-PATCH (%d-%d@%d..%d): %d; timbre=%d (%s) %02X%02X%02X%02X%02X%02X%02X%02X", first, last, off, off + len, i, patchAbsTimbreNum, instrumentName, n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7]); + // FIXME:KG: The below is definitely dodgy. We just guess that this is the patch that the part was using + // based on a timbre match (but many patches could have the same timbre!) + // If this refresh is really correct, we should store the patch number in use by each part. + /* + for (int part = 0; part < 8; part++) { + if (parts[part] != NULL) { + int partPatchAbsTimbreNum = mt32ram.patchSettings[part].patch.timbreGroup * 64 + mt32ram.patchSettings[part].patch.timbreNum; + if (parts[part]->getAbsTimbreNum() == patchAbsTimbreNum) { + parts[part]->setPatch(patch); + parts[part]->RefreshPatch(); + } + } + } + */ + } + break; + case MR_Timbres: + // Timbres + first += 128; + last += 128; + for (unsigned int m = 0; m < len; m++) + ((Bit8u *)&mt32ram.timbres[first])[off + m] = data[m]; + for (unsigned int i = first; i <= last; i++) { + char instrumentName[11]; + memcpy(instrumentName, mt32ram.timbres[i].timbre.common.name, 10); + instrumentName[10] = 0; + printDebug("WRITE-TIMBRE (%d-%d@%d..%d): %d; name=\"%s\"", first, last, off, off + len, i, instrumentName); + // FIXME:KG: Not sure if the stuff below should be done (for rhythm and/or parts)... + // Does the real MT-32 automatically do this? + for (unsigned int part = 0; part < 9; part++) { + if (parts[part] != NULL) { + parts[part]->refreshTimbre(i); + } + } + } + break; + case MR_System: + for (unsigned int m = 0; m < len; m++) + ((Bit8u *)&mt32ram.system)[m + off] = data[m]; + + report(ReportType_devReconfig, NULL); + + printDebug("WRITE-SYSTEM:"); + refreshSystem(); + break; + case MR_Display: + char buf[MAX_SYSEX_SIZE]; + memcpy(&buf, &data[0], len); + buf[len] = 0; + printDebug("WRITE-LCD: %s", buf); + report(ReportType_lcdMessage, buf); + break; + case MR_Reset: + printDebug("RESET"); + report(ReportType_devReset, NULL); + partialManager->deactivateAll(); + mt32ram = mt32default; + for (int i = 0; i < 9; i++) { + parts[i]->refresh(); + } + isEnabled = false; + break; + } +} + +bool Synth::refreshSystem() { + memset(chantable, -1, sizeof(chantable)); + + for (unsigned int i = 0; i < 9; i++) { + //LOG(LOG_MISC|LOG_ERROR,"Part %d set to MIDI channel %d",i,mt32ram.system.chanAssign[i]); + if (mt32ram.system.chanAssign[i] == 16 && parts[i] != NULL) { + parts[i]->allSoundOff(); + } else { + chantable[(int)mt32ram.system.chanAssign[i]] = (char)i; + } + } + //FIXME:KG: This is just an educated guess. + // The LAPC-I documentation claims a range of 427.5Hz-452.6Hz (similar to what we have here) + // The MT-32 documentation claims a range of 432.1Hz-457.6Hz + masterTune = 440.0f * powf(2.0f, (mt32ram.system.masterTune - 64.0f) / (128.0f * 12.0f)); + printDebug(" Master Tune: %f", masterTune); + printDebug(" Reverb: mode=%d, time=%d, level=%d", mt32ram.system.reverbMode, mt32ram.system.reverbTime, mt32ram.system.reverbLevel); + report(ReportType_newReverbMode, &mt32ram.system.reverbMode); + report(ReportType_newReverbTime, &mt32ram.system.reverbTime); + report(ReportType_newReverbLevel, &mt32ram.system.reverbLevel); + + if (myProp.useDefaultReverb) { + initReverb(mt32ram.system.reverbMode, mt32ram.system.reverbTime, mt32ram.system.reverbLevel); + } else { + initReverb(myProp.reverbType, myProp.reverbTime, mt32ram.system.reverbLevel); + } + + Bit8u *rset = mt32ram.system.reserveSettings; + printDebug(" Partial reserve: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]); + int pr = partialManager->setReserve(rset); + if (pr != 32) + printDebug(" (Partial Reserve Table with less than 32 partials reserved!)"); + rset = mt32ram.system.chanAssign; + printDebug(" Part assign: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]); + printDebug(" Master volume: %d", mt32ram.system.masterVol); + masterVolume = (Bit16u)(mt32ram.system.masterVol * 32767 / 100); + if (!tables.init(this, pcmWaves, (float)myProp.sampleRate, masterTune)) { + report(ReportType_errorSampleRate, NULL); + return false; + } + return true; +} + +bool Synth::dumpTimbre(File *file, const TimbreParam *timbre, Bit32u address) { + // Sysex header + if (!file->writeBit8u(0xF0)) + return false; + if (!file->writeBit8u(0x41)) + return false; + if (!file->writeBit8u(0x10)) + return false; + if (!file->writeBit8u(0x16)) + return false; + if (!file->writeBit8u(0x12)) + return false; + + char lsb = (char)(address & 0x7f); + char isb = (char)((address >> 7) & 0x7f); + char msb = (char)(((address >> 14) & 0x7f) | 0x08); + + //Address + if (!file->writeBit8u(msb)) + return false; + if (!file->writeBit8u(isb)) + return false; + if (!file->writeBit8u(lsb)) + return false; + + //Data + if (file->write(timbre, 246) != 246) + return false; + + //Checksum + unsigned char checksum = calcSysexChecksum((const Bit8u *)timbre, 246, msb + isb + lsb); + if (!file->writeBit8u(checksum)) + return false; + + //End of sysex + if (!file->writeBit8u(0xF7)) + return false; + return true; +} + +int Synth::dumpTimbres(const char *filename, int start, int len) { + File *file = openFile(filename, File::OpenMode_write); + if (file == NULL) + return -1; + + for (int timbreNum = start; timbreNum < start + len; timbreNum++) { + int useaddr = (timbreNum - start) * 256; + TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre; + if (!dumpTimbre(file, timbre, useaddr)) + break; + } + closeFile(file); + return 0; +} + +void ProduceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume) { +#if MT32EMU_USE_MMX > 2 + //FIXME:KG: This appears to introduce crackle + int donelen = i386_produceOutput1(useBuf, stream, len, volume); + len -= donelen; + stream += donelen * 2; + useBuf += donelen * 2; +#endif + int end = len * 2; + while (end--) { + *stream = *stream + (Bit16s)(((Bit32s)*useBuf++ * (Bit32s)volume)>>15); + stream++; + } +} + +void Synth::render(Bit16s *stream, Bit32u len) { + memset(stream, 0, len * sizeof (Bit16s) * 2); + if (!isEnabled) + return; + while (len > 0) { + Bit32u thisLen = len > MAX_SAMPLE_OUTPUT ? MAX_SAMPLE_OUTPUT : len; + doRender(stream, thisLen); + len -= thisLen; + stream += 2 * thisLen; + } +} + +void Synth::doRender(Bit16s *stream, Bit32u len) { + partialManager->ageAll(); + + if (myProp.useReverb) { + for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + if (partialManager->shouldReverb(i)) { + if (partialManager->produceOutput(i, &tmpBuffer[0], len)) { + ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume); + } + } + } + Bit32u m = 0; + for (unsigned int i = 0; i < len; i++) { + sndbufl[i] = (float)stream[m] / 32767.0f; + m++; + sndbufr[i] = (float)stream[m] / 32767.0f; + m++; + } + reverbModel->processreplace(sndbufl, sndbufr, outbufl, outbufr, len, 1); + m=0; + for (unsigned int i = 0; i < len; i++) { + stream[m] = (Bit16s)(outbufl[i] * 32767.0f); + m++; + stream[m] = (Bit16s)(outbufr[i] * 32767.0f); + m++; + } + for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + if (!partialManager->shouldReverb(i)) { + if (partialManager->produceOutput(i, &tmpBuffer[0], len)) { + ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume); + } + } + } + } else { + for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + if (partialManager->produceOutput(i, &tmpBuffer[0], len)) + ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume); + } + } + + partialManager->clearAlreadyOutputed(); + +#if MT32EMU_MONITOR_PARTIALS == 1 + samplepos += len; + if (samplepos > myProp.SampleRate * 5) { + samplepos = 0; + int partialUsage[9]; + partialManager->GetPerPartPartialUsage(partialUsage); + printDebug("1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d", partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7]); + printDebug("Rhythm: %02d TOTAL: %02d", partialUsage[8], MT32EMU_MAX_PARTIALS - partialManager->GetFreePartialCount()); + } +#endif +} + +const Partial *Synth::getPartial(unsigned int partialNum) const { + return partialManager->getPartial(partialNum); +} + +const Part *Synth::getPart(unsigned int partNum) const { + if (partNum > 8) + return NULL; + return parts[partNum]; +} + +} diff --git a/engines/sci/sfx/softseq/mt32/synth.h b/engines/sci/sfx/softseq/mt32/synth.h new file mode 100644 index 0000000000..9d57c8d3cd --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/synth.h @@ -0,0 +1,300 @@ +/* Copyright (c) 2003-2005 Various contributors + * + * 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. + */ + +#ifndef MT32EMU_SYNTH_H +#define MT32EMU_SYNTH_H + +#include + +class revmodel; + +namespace MT32Emu { + +class File; +class TableInitialiser; +class Partial; +class PartialManager; +class Part; + +enum ReportType { + // Errors + ReportType_errorControlROM = 1, + ReportType_errorPCMROM, + ReportType_errorSampleRate, + + // Progress + ReportType_progressInit, + + // HW spec + ReportType_availableSSE, + ReportType_available3DNow, + ReportType_usingSSE, + ReportType_using3DNow, + + // General info + ReportType_lcdMessage, + ReportType_devReset, + ReportType_devReconfig, + ReportType_newReverbMode, + ReportType_newReverbTime, + ReportType_newReverbLevel +}; + +struct SynthProperties { + // Sample rate to use in mixing + int sampleRate; + + // Flag to activate reverb. True = use reverb, False = no reverb + bool useReverb; + // True to use software set reverb settings, False to set reverb settings in + // following parameters + bool useDefaultReverb; + // When not using the default settings, this specifies one of the 4 reverb types + // 1 = Room 2 = Hall 3 = Plate 4 = Tap + unsigned char reverbType; + // This specifies the delay time, from 0-7 (not sure of the actual MT-32's measurement) + unsigned char reverbTime; + // This specifies the reverb level, from 0-7 (not sure of the actual MT-32's measurement) + unsigned char reverbLevel; + // The name of the directory in which the ROM and data files are stored (with trailing slash/backslash) + // Not used if "openFile" is set. May be NULL in any case. + char *baseDir; + // This is used as the first argument to all callbacks + void *userData; + // Callback for reporting various errors and information. May be NULL + int (*report)(void *userData, ReportType type, const void *reportData); + // Callback for debug messages, in vprintf() format + void (*printDebug)(void *userData, const char *fmt, va_list list); + // Callback for providing an implementation of File, opened and ready for use + // May be NULL, in which case a default implementation will be used. + File *(*openFile)(void *userData, const char *filename, File::OpenMode mode); + // Callback for closing a File. May be NULL, in which case the File will automatically be close()d/deleted. + void (*closeFile)(void *userData, File *file); +}; + +// This is the specification of the Callback routine used when calling the RecalcWaveforms +// function +typedef void (*recalcStatusCallback)(int percDone); + +// This external function recreates the base waveform file (waveforms.raw) using a specifed +// sampling rate. The callback routine provides interactivity to let the user know what +// percentage is complete in regenerating the waveforms. When a NULL pointer is used as the +// callback routine, no status is reported. +bool RecalcWaveforms(char * baseDir, int sampRate, recalcStatusCallback callBack); + +typedef float (*iir_filter_type)(float input,float *hist1_ptr, float *coef_ptr); + +const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41; + +const Bit8u SYSEX_MDL_MT32 = 0x16; +const Bit8u SYSEX_MDL_D50 = 0x14; + +const Bit8u SYSEX_CMD_RQ1 = 0x11; // Request data #1 +const Bit8u SYSEX_CMD_DT1 = 0x12; // Data set 1 +const Bit8u SYSEX_CMD_WSD = 0x40; // Want to send data +const Bit8u SYSEX_CMD_RQD = 0x41; // Request data +const Bit8u SYSEX_CMD_DAT = 0x42; // Data set +const Bit8u SYSEX_CMD_ACK = 0x43; // Acknowledge +const Bit8u SYSEX_CMD_EOD = 0x45; // End of data +const Bit8u SYSEX_CMD_ERR = 0x4E; // Communications error +const Bit8u SYSEX_CMD_RJC = 0x4F; // Rejection + +const unsigned int CONTROL_ROM_SIZE = 64 * 1024; + +struct ControlROMPCMStruct +{ + Bit8u pos; + Bit8u len; + Bit8u pitchLSB; + Bit8u pitchMSB; +}; + +struct ControlROMMap { + Bit16u idPos; + Bit16u idLen; + const char *idBytes; + Bit16u pcmTable; + Bit16u pcmCount; + Bit16u timbreAMap; + Bit16u timbreAOffset; + Bit16u timbreBMap; + Bit16u timbreBOffset; + Bit16u timbreRMap; + Bit16u timbreRCount; + Bit16u rhythmSettings; + Bit16u rhythmSettingsCount; + Bit16u reserveSettings; + Bit16u panSettings; + Bit16u programSettings; +}; + +enum MemoryRegionType { + MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset +}; + +class MemoryRegion { +public: + MemoryRegionType type; + Bit32u startAddr, entrySize, entries; + + int lastTouched(Bit32u addr, Bit32u len) const { + return (offset(addr) + len - 1) / entrySize; + } + int firstTouchedOffset(Bit32u addr) const { + return offset(addr) % entrySize; + } + int firstTouched(Bit32u addr) const { + return offset(addr) / entrySize; + } + Bit32u regionEnd() const { + return startAddr + entrySize * entries; + } + bool contains(Bit32u addr) const { + return addr >= startAddr && addr < regionEnd(); + } + int offset(Bit32u addr) const { + return addr - startAddr; + } + Bit32u getClampedLen(Bit32u addr, Bit32u len) const { + if (addr + len > regionEnd()) + return regionEnd() - addr; + return len; + } + Bit32u next(Bit32u addr, Bit32u len) const { + if (addr + len > regionEnd()) { + return regionEnd() - addr; + } + return 0; + } +}; + + +class Synth { +friend class Part; +friend class RhythmPart; +friend class Partial; +friend class Tables; +private: + bool isEnabled; + + iir_filter_type iirFilter; + + PCMWaveEntry *pcmWaves; // Array + + const ControlROMMap *controlROMMap; + Bit8u controlROMData[CONTROL_ROM_SIZE]; + Bit16s *pcmROMData; + int pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM + + Bit8s chantable[32]; + + #if MT32EMU_MONITOR_PARTIALS == 1 + static Bit32s samplepos = 0; + #endif + + Tables tables; + + MemParams mt32ram, mt32default; + + revmodel *reverbModel; + + float masterTune; + Bit16u masterVolume; + + bool isOpen; + + PartialManager *partialManager; + Part *parts[9]; + + Bit16s tmpBuffer[MAX_SAMPLE_OUTPUT * 2]; + float sndbufl[MAX_SAMPLE_OUTPUT]; + float sndbufr[MAX_SAMPLE_OUTPUT]; + float outbufl[MAX_SAMPLE_OUTPUT]; + float outbufr[MAX_SAMPLE_OUTPUT]; + + SynthProperties myProp; + + bool loadPreset(File *file); + void initReverb(Bit8u newRevMode, Bit8u newRevTime, Bit8u newRevLevel); + void doRender(Bit16s * stream, Bit32u len); + + void playAddressedSysex(unsigned char channel, const Bit8u *sysex, Bit32u len); + void readSysex(unsigned char channel, const Bit8u *sysex, Bit32u len); + void writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data); + void readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data); + + bool loadControlROM(const char *filename); + bool loadPCMROM(const char *filename); + bool dumpTimbre(File *file, const TimbreParam *timbre, Bit32u addr); + int dumpTimbres(const char *filename, int start, int len); + + bool initPCMList(Bit16u mapAddress, Bit16u count); + bool initRhythmTimbres(Bit16u mapAddress, Bit16u count); + bool initTimbres(Bit16u mapAddress, Bit16u offset, int startTimbre); + bool initRhythmTimbre(int drumNum, const Bit8u *mem, unsigned int memLen); + bool refreshSystem(); + +protected: + int report(ReportType type, const void *reportData); + File *openFile(const char *filename, File::OpenMode mode); + void closeFile(File *file); + void printDebug(const char *fmt, ...); + +public: + static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum); + + Synth(); + ~Synth(); + + // Used to initialise the MT-32. Must be called before any other function. + // Returns true if initialization was sucessful, otherwise returns false. + bool open(SynthProperties &useProp); + + // Closes the MT-32 and deallocates any memory used by the synthesizer + void close(void); + + // Sends a 4-byte MIDI message to the MT-32 for immediate playback + void playMsg(Bit32u msg); + void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity); + + // Sends a string of Sysex commands to the MT-32 for immediate interpretation + // The length is in bytes + void playSysex(const Bit8u *sysex, Bit32u len); + void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len); + void playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len); + void writeSysex(unsigned char channel, const Bit8u *sysex, Bit32u len); + + // This callback routine is used to have the MT-32 generate samples to the specified + // output stream. The length is in whole samples, not bytes. (I.E. in 16-bit stereo, + // one sample is 4 bytes) + void render(Bit16s * stream, Bit32u len); + + const Partial *getPartial(unsigned int partialNum) const; + + void readMemory(Bit32u addr, Bit32u len, Bit8u *data); + + // partNum should be 0..7 for Part 1..8, or 8 for Rhythm + const Part *getPart(unsigned int partNum) const; +}; + +} + +#endif diff --git a/engines/sci/sfx/softseq/mt32/tables.cpp b/engines/sci/sfx/softseq/mt32/tables.cpp new file mode 100644 index 0000000000..4591ea22e3 --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/tables.cpp @@ -0,0 +1,749 @@ +/* Copyright (c) 2003-2005 Various contributors + * + * 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. + */ + +#include +#include +#include + +#include "mt32emu.h" + +#ifdef MACOSX +// Older versions of Mac OS X didn't supply a powf function. To ensure +// binary compatibility, we force using pow instead of powf (the only +// potential drawback is that it might be a little bit slower). +#define powf pow +#endif + +#define FIXEDPOINT_MAKE(x, point) ((Bit32u)((1 << point) * x)) + +namespace MT32Emu { + +//Amplitude time velocity follow exponential coefficients +static const double tvcatconst[5] = {0.0, 0.002791309, 0.005942882, 0.012652792, 0.026938637}; +static const double tvcatmult[5] = {1.0, 1.072662811, 1.169129367, 1.288579123, 1.229630539}; + +// These are division constants for the TVF depth key follow +static const Bit32u depexp[5] = {3000, 950, 485, 255, 138}; + +//Envelope time keyfollow exponential coefficients +static const double tkcatconst[5] = {0.0, 0.005853144, 0.011148054, 0.019086143, 0.043333215}; +static const double tkcatmult[5] = {1.0, 1.058245688, 1.048488989, 1.016049301, 1.097538067}; + +// Begin filter stuff + +// Pre-warp the coefficients of a numerator or denominator. +// Note that a0 is assumed to be 1, so there is no wrapping +// of it. +static void prewarp(double *a1, double *a2, double fc, double fs) { + double wp; + + wp = 2.0 * fs * tan(DOUBLE_PI * fc / fs); + + *a2 = *a2 / (wp * wp); + *a1 = *a1 / wp; +} + +// Transform the numerator and denominator coefficients +// of s-domain biquad section into corresponding +// z-domain coefficients. +// +// Store the 4 IIR coefficients in array pointed by coef +// in following order: +// beta1, beta2 (denominator) +// alpha1, alpha2 (numerator) +// +// Arguments: +// a0-a2 - s-domain numerator coefficients +// b0-b2 - s-domain denominator coefficients +// k - filter gain factor. initially set to 1 +// and modified by each biquad section in such +// a way, as to make it the coefficient by +// which to multiply the overall filter gain +// in order to achieve a desired overall filter gain, +// specified in initial value of k. +// fs - sampling rate (Hz) +// coef - array of z-domain coefficients to be filled in. +// +// Return: +// On return, set coef z-domain coefficients +static void bilinear(double a0, double a1, double a2, double b0, double b1, double b2, double *k, double fs, float *coef) { + double ad, bd; + + // alpha (Numerator in s-domain) + ad = 4. * a2 * fs * fs + 2. * a1 * fs + a0; + // beta (Denominator in s-domain) + bd = 4. * b2 * fs * fs + 2. * b1* fs + b0; + + // update gain constant for this section + *k *= ad/bd; + + // Denominator + *coef++ = (float)((2. * b0 - 8. * b2 * fs * fs) / bd); // beta1 + *coef++ = (float)((4. * b2 * fs * fs - 2. * b1 * fs + b0) / bd); // beta2 + + // Nominator + *coef++ = (float)((2. * a0 - 8. * a2 * fs * fs) / ad); // alpha1 + *coef = (float)((4. * a2 * fs * fs - 2. * a1 * fs + a0) / ad); // alpha2 +} + +// a0-a2: numerator coefficients +// b0-b2: denominator coefficients +// fc: Filter cutoff frequency +// fs: sampling rate +// k: overall gain factor +// coef: pointer to 4 iir coefficients +static void szxform(double *a0, double *a1, double *a2, double *b0, double *b1, double *b2, double fc, double fs, double *k, float *coef) { + // Calculate a1 and a2 and overwrite the original values + prewarp(a1, a2, fc, fs); + prewarp(b1, b2, fc, fs); + bilinear(*a0, *a1, *a2, *b0, *b1, *b2, k, fs, coef); +} + +static void initFilter(float fs, float fc, float *icoeff, float Q) { + float *coef; + double a0, a1, a2, b0, b1, b2; + + double k = 1.5; // Set overall filter gain factor + coef = icoeff + 1; // Skip k, or gain + + // Section 1 + a0 = 1.0; + a1 = 0; + a2 = 0; + b0 = 1.0; + b1 = 0.765367 / Q; // Divide by resonance or Q + b2 = 1.0; + szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef); + coef += 4; // Point to next filter section + + // Section 2 + a0 = 1.0; + a1 = 0; + a2 = 0; + b0 = 1.0; + b1 = 1.847759 / Q; + b2 = 1.0; + szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef); + + icoeff[0] = (float)k; +} + +void Tables::initFiltCoeff(float samplerate) { + for (int j = 0; j < FILTERGRAN; j++) { + for (int res = 0; res < 31; res++) { + float tres = resonanceFactor[res]; + initFilter((float)samplerate, (((float)(j+1.0)/FILTERGRAN)) * ((float)samplerate/2), filtCoeff[j][res], tres); + } + } +} + +void Tables::initEnvelopes(float samplerate) { + for (int lf = 0; lf <= 100; lf++) { + float elf = (float)lf; + + // General envelope + // This formula fits observation of the CM-32L by +/- 0.03s or so for the second time value in the filter, + // when all other times were 0 and all levels were 100. Note that variations occur depending on the level + // delta of the section, which we're not fully emulating. + float seconds = powf(2.0f, (elf / 8.0f) + 7.0f) / 32768.0f; + int samples = (int)(seconds * samplerate); + envTime[lf] = samples; + + // Cap on envelope times depending on the level delta + if (elf == 0) { + envDeltaMaxTime[lf] = 63; + } else { + float cap = 11.0f * (float)log(elf) + 64; + if (cap > 100.0f) { + cap = 100.0f; + } + envDeltaMaxTime[lf] = (int)cap; + } + + + // This (approximately) represents the time durations when the target level is 0. + // Not sure why this is a special case, but it's seen to be from the real thing. + seconds = powf(2, (elf / 8.0f) + 6) / 32768.0f; + envDecayTime[lf] = (int)(seconds * samplerate); + + // I am certain of this: Verified by hand LFO log + lfoPeriod[lf] = (Bit32u)(((float)samplerate) / (powf(1.088883372f, (float)lf) * 0.021236044f)); + } +} + +void Tables::initMT32ConstantTables(Synth *synth) { + int lf; + synth->printDebug("Initialising Pitch Tables"); + for (lf = -108; lf <= 108; lf++) { + tvfKeyfollowMult[lf + 108] = (int)(256 * powf(2.0f, (float)(lf / 24.0f))); + //synth->printDebug("KT %d = %d", f, keytable[f+108]); + } + + for (int res = 0; res < 31; res++) { + resonanceFactor[res] = powf((float)res / 30.0f, 5.0f) + 1.0f; + } + + int period = 65536; + + for (int ang = 0; ang < period; ang++) { + int halfang = (period / 2); + int angval = ang % halfang; + float tval = (((float)angval / (float)halfang) - 0.5f) * 2; + if (ang >= halfang) + tval = -tval; + sintable[ang] = (Bit16s)(tval * 50.0f) + 50; + } + + int velt, dep; + float tempdep; + for (velt = 0; velt < 128; velt++) { + for (dep = 0; dep < 5; dep++) { + if (dep > 0) { + float ff = (float)(exp(3.5f * tvcatconst[dep] * (59.0f - (float)velt)) * tvcatmult[dep]); + tempdep = 256.0f * ff; + envTimeVelfollowMult[dep][velt] = (int)tempdep; + //if ((velt % 16) == 0) { + // synth->printDebug("Key %d, depth %d, factor %d", velt, dep, (int)tempdep); + //} + } else + envTimeVelfollowMult[dep][velt] = 256; + } + + for (dep = -7; dep < 8; dep++) { + float fldep = (float)abs(dep) / 7.0f; + fldep = powf(fldep,2.5f); + if (dep < 0) + fldep = fldep * -1.0f; + pwVelfollowAdd[dep+7][velt] = Bit32s((fldep * (float)velt * 100) / 128.0); + } + } + + for (dep = 0; dep <= 100; dep++) { + for (velt = 0; velt < 128; velt++) { + float fdep = (float)dep * 0.000347013f; // Another MT-32 constant + float fv = ((float)velt - 64.0f)/7.26f; + float flogdep = powf(10, fdep * fv); + float fbase; + + if (velt > 64) + synth->tables.tvfVelfollowMult[velt][dep] = (int)(flogdep * 256.0); + else { + //lff = 1 - (pow(((128.0 - (float)lf) / 64.0),.25) * ((float)velt / 96)); + fbase = 1 - (powf(((float)dep / 100.0f),.25f) * ((float)(64-velt) / 96.0f)); + synth->tables.tvfVelfollowMult[velt][dep] = (int)(fbase * 256.0); + } + //synth->printDebug("Filvel dep %d velt %d = %x", dep, velt, filveltable[velt][dep]); + } + } + + for (lf = 0; lf < 128; lf++) { + float veloFract = lf / 127.0f; + for (int velsens = 0; velsens <= 100; velsens++) { + float sensFract = (velsens - 50) / 50.0f; + if (velsens < 50) { + tvaVelfollowMult[lf][velsens] = FIXEDPOINT_MAKE(1.0f / powf(2.0f, veloFract * -sensFract * 127.0f / 20.0f), 8); + } else { + tvaVelfollowMult[lf][velsens] = FIXEDPOINT_MAKE(1.0f / powf(2.0f, (1.0f - veloFract) * sensFract * 127.0f / 20.0f), 8); + } + } + } + + for (lf = 0; lf <= 100; lf++) { + // Converts the 0-100 range used by the MT-32 to volume multiplier + volumeMult[lf] = FIXEDPOINT_MAKE(powf((float)lf / 100.0f, FLOAT_LN), 7); + } + + for (lf = 0; lf <= 100; lf++) { + float mv = lf / 100.0f; + float pt = mv - 0.5f; + if (pt < 0) + pt = 0; + + // Original (CC version) + //pwFactor[lf] = (int)(pt * 210.04f) + 128; + + // Approximation from sample comparison + pwFactor[lf] = (int)(pt * 179.0f) + 128; + } + + for (unsigned int i = 0; i < MAX_SAMPLE_OUTPUT; i++) { + int myRand; + myRand = rand(); + //myRand = ((myRand - 16383) * 7168) >> 16; + // This one is slower but works with all values of RAND_MAX + myRand = (int)((myRand - RAND_MAX / 2) / (float)RAND_MAX * (7168 / 2)); + //FIXME:KG: Original ultimately set the lowest two bits to 0, for no obvious reason + noiseBuf[i] = (Bit16s)myRand; + } + + float tdist; + float padjtable[51]; + for (lf = 0; lf <= 50; lf++) { + if (lf == 0) + padjtable[lf] = 7; + else if (lf == 1) + padjtable[lf] = 6; + else if (lf == 2) + padjtable[lf] = 5; + else if (lf == 3) + padjtable[lf] = 4; + else if (lf == 4) + padjtable[lf] = 4 - (0.333333f); + else if (lf == 5) + padjtable[lf] = 4 - (0.333333f * 2); + else if (lf == 6) + padjtable[lf] = 3; + else if ((lf > 6) && (lf <= 12)) { + tdist = (lf-6.0f) / 6.0f; + padjtable[lf] = 3.0f - tdist; + } else if ((lf > 12) && (lf <= 25)) { + tdist = (lf - 12.0f) / 13.0f; + padjtable[lf] = 2.0f - tdist; + } else { + tdist = (lf - 25.0f) / 25.0f; + padjtable[lf] = 1.0f - tdist; + } + //synth->printDebug("lf %d = padj %f", lf, padjtable[lf]); + } + + float lfp, depf, finalval, tlf; + int depat, pval, depti; + for (lf = 0; lf <= 10; lf++) { + // I believe the depth is cubed or something + + for (depat = 0; depat <= 100; depat++) { + if (lf > 0) { + depti = abs(depat - 50); + tlf = (float)lf - padjtable[depti]; + if (tlf < 0) + tlf = 0; + lfp = (float)exp(0.713619942f * tlf) / 407.4945111f; + + if (depat < 50) + finalval = 4096.0f * powf(2, -lfp); + else + finalval = 4096.0f * powf(2, lfp); + pval = (int)finalval; + + pitchEnvVal[lf][depat] = pval; + //synth->printDebug("lf %d depat %d pval %d tlf %f lfp %f", lf,depat,pval, tlf, lfp); + } else { + pitchEnvVal[lf][depat] = 4096; + //synth->printDebug("lf %d depat %d pval 4096", lf, depat); + } + } + } + for (lf = 0; lf <= 100; lf++) { + // It's linear - verified on MT-32 - one of the few things linear + lfp = ((float)lf * 0.1904f) / 310.55f; + + for (depat = 0; depat <= 100; depat++) { + depf = ((float)depat - 50.0f) / 50.0f; + //finalval = pow(2, lfp * depf * .5); + finalval = 4096.0f + (4096.0f * lfp * depf); + + pval = (int)finalval; + + lfoShift[lf][depat] = pval; + + //synth->printDebug("lf %d depat %d pval %x", lf,depat,pval); + } + } + + for (lf = 0; lf <= 12; lf++) { + for (int distval = 0; distval < 128; distval++) { + float amplog, dval; + if (lf == 0) { + amplog = 0; + dval = 1; + tvaBiasMult[lf][distval] = 256; + } else { + /* + amplog = powf(1.431817011f, (float)lf) / FLOAT_PI; + dval = ((128.0f - (float)distval) / 128.0f); + amplog = exp(amplog); + dval = powf(amplog, dval) / amplog; + tvaBiasMult[lf][distval] = (int)(dval * 256.0); + */ + // Lets assume for a second it's linear + + // Distance of full volume reduction + amplog = (float)(12.0f / (float)lf) * 24.0f; + if (distval > amplog) { + tvaBiasMult[lf][distval] = 0; + } else { + dval = (amplog - (float)distval) / amplog; + tvaBiasMult[lf][distval] = (int)(dval * 256.0f); + } + } + //synth->printDebug("Ampbias lf %d distval %d = %f (%x) %f", lf, distval, dval, tvaBiasMult[lf][distval],amplog); + } + } + + for (lf = 0; lf <= 14; lf++) { + for (int distval = 0; distval < 128; distval++) { + float filval = fabsf((float)((lf - 7) * 12) / 7.0f); + float amplog, dval; + if (lf == 7) { + amplog = 0; + dval = 1; + tvfBiasMult[lf][distval] = 256; + } else { + //amplog = pow(1.431817011, filval) / FLOAT_PI; + amplog = powf(1.531817011f, filval) / FLOAT_PI; + dval = (128.0f - (float)distval) / 128.0f; + amplog = (float)exp(amplog); + dval = powf(amplog,dval)/amplog; + if (lf < 8) { + tvfBiasMult[lf][distval] = (int)(dval * 256.0f); + } else { + dval = powf(dval, 0.3333333f); + if (dval < 0.01f) + dval = 0.01f; + dval = 1 / dval; + tvfBiasMult[lf][distval] = (int)(dval * 256.0f); + } + } + //synth->printDebug("Fbias lf %d distval %d = %f (%x) %f", lf, distval, dval, tvfBiasMult[lf][distval],amplog); + } + } +} + +// Per-note table initialisation follows + +static void initSaw(NoteLookup *noteLookup, Bit32s div2) { + int tmpdiv = div2 << 16; + for (int rsaw = 0; rsaw <= 100; rsaw++) { + float fsaw; + if (rsaw < 50) + fsaw = 50.0f; + else + fsaw = (float)rsaw; + + //(66 - (((A8 - 50) / 50) ^ 0.63) * 50) / 132 + float sawfact = (66.0f - (powf((fsaw - 50.0f) / 50.0f, 0.63f) * 50.0f)) / 132.0f; + noteLookup->sawTable[rsaw] = (int)(sawfact * (float)tmpdiv) >> 16; + //synth->printDebug("F %d divtable %d saw %d sawtable %d", f, div, rsaw, sawtable[f][rsaw]); + } +} + +static void initDep(KeyLookup *keyLookup, float f) { + for (int dep = 0; dep < 5; dep++) { + if (dep == 0) { + keyLookup->envDepthMult[dep] = 256; + keyLookup->envTimeMult[dep] = 256; + } else { + float depfac = 3000.0f; + float ff, tempdep; + depfac = (float)depexp[dep]; + + ff = (f - (float)MIDDLEC) / depfac; + tempdep = powf(2, ff) * 256.0f; + keyLookup->envDepthMult[dep] = (int)tempdep; + + ff = (float)(exp(tkcatconst[dep] * ((float)MIDDLEC - f)) * tkcatmult[dep]); + keyLookup->envTimeMult[dep] = (int)(ff * 256.0f); + } + } + //synth->printDebug("F %f d1 %x d2 %x d3 %x d4 %x d5 %x", f, noteLookup->fildepTable[0], noteLookup->fildepTable[1], noteLookup->fildepTable[2], noteLookup->fildepTable[3], noteLookup->fildepTable[4]); +} + +Bit16s Tables::clampWF(Synth *synth, const char *n, float ampVal, double input) { + Bit32s x = (Bit32s)(input * ampVal); + if (x < -ampVal - 1) { + synth->printDebug("%s==%d<-WGAMP-1!", n, x); + x = (Bit32s)(-ampVal - 1); + } else if (x > ampVal) { + synth->printDebug("%s==%d>WGAMP!", n, x); + x = (Bit32s)ampVal; + } + return (Bit16s)x; +} + +File *Tables::initWave(Synth *synth, NoteLookup *noteLookup, float ampVal, float div2, File *file) { + int iDiv2 = (int)div2; + noteLookup->waveformSize[0] = iDiv2 << 1; + noteLookup->waveformSize[1] = iDiv2 << 1; + noteLookup->waveformSize[2] = iDiv2 << 2; + for (int i = 0; i < 3; i++) { + if (noteLookup->waveforms[i] == NULL) { + noteLookup->waveforms[i] = new Bit16s[noteLookup->waveformSize[i]]; + } + } + if (file != NULL) { + for (int i = 0; i < 3 && file != NULL; i++) { + size_t len = noteLookup->waveformSize[i]; + for (unsigned int j = 0; j < len; j++) { + if (!file->readBit16u((Bit16u *)¬eLookup->waveforms[i][j])) { + synth->printDebug("Error reading wave file cache!"); + file->close(); + file = NULL; + break; + } + } + } + } + if (file == NULL) { + double sd = DOUBLE_PI / div2; + + for (int fa = 0; fa < (iDiv2 << 1); fa++) { + // sa ranges from 0 to 2PI + double sa = fa * sd; + + // Calculate a sample for the bandlimited sawtooth wave + double saw = 0.0; + int sincs = iDiv2 >> 1; + double sinus = 1.0; + for (int sincNum = 1; sincNum <= sincs; sincNum++) { + saw += sin(sinus * sa) / sinus; + sinus++; + } + + // This works pretty well + // Multiplied by 0.84 so that the spikes caused by bandlimiting don't overdrive the amplitude + noteLookup->waveforms[0][fa] = clampWF(synth, "saw", ampVal, -saw / (0.5 * DOUBLE_PI) * 0.84); + noteLookup->waveforms[1][fa] = clampWF(synth, "cos", ampVal, -cos(sa / 2.0)); + noteLookup->waveforms[2][fa * 2] = clampWF(synth, "cosoff_0", ampVal, -cos(sa - DOUBLE_PI)); + noteLookup->waveforms[2][fa * 2 + 1] = clampWF(synth, "cosoff_1", ampVal, -cos((sa + (sd / 2)) - DOUBLE_PI)); + } + } + return file; +} + +static void initFiltTable(NoteLookup *noteLookup, float freq, float rate) { + for (int tr = 0; tr <= 200; tr++) { + float ftr = (float)tr; + + // Verified exact on MT-32 + if (tr > 100) + ftr = 100.0f + (powf((ftr - 100.0f) / 100.0f, 3.0f) * 100.0f); + + // I think this is the one + float brsq = powf(10.0f, (tr / 50.0f) - 1.0f); + noteLookup->filtTable[0][tr] = (int)((freq * brsq) / (rate / 2) * FILTERGRAN); + if (noteLookup->filtTable[0][tr]>=((FILTERGRAN*15)/16)) + noteLookup->filtTable[0][tr] = ((FILTERGRAN*15)/16); + + float brsa = powf(10.0f, ((tr / 55.0f) - 1.0f)) / 2.0f; + noteLookup->filtTable[1][tr] = (int)((freq * brsa) / (rate / 2) * FILTERGRAN); + if (noteLookup->filtTable[1][tr]>=((FILTERGRAN*15)/16)) + noteLookup->filtTable[1][tr] = ((FILTERGRAN*15)/16); + } +} + +static void initNFiltTable(NoteLookup *noteLookup, float freq, float rate) { + for (int cf = 0; cf <= 100; cf++) { + float cfmult = (float)cf; + + for (int tf = 0;tf <= 100; tf++) { + float tfadd = (float)tf; + + //float freqsum = exp((cfmult + tfadd) / 30.0f) / 4.0f; + //float freqsum = 0.15f * exp(0.45f * ((cfmult + tfadd) / 10.0f)); + + float freqsum = powf(2.0f, ((cfmult + tfadd) - 40.0f) / 16.0f); + + noteLookup->nfiltTable[cf][tf] = (int)((freq * freqsum) / (rate / 2) * FILTERGRAN); + if (noteLookup->nfiltTable[cf][tf] >= ((FILTERGRAN * 15) / 16)) + noteLookup->nfiltTable[cf][tf] = ((FILTERGRAN * 15) / 16); + } + } +} + +File *Tables::initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float masterTune, PCMWaveEntry *pcmWaves, File *file) { + float freq = (float)(masterTune * pow(2.0, ((double)note - MIDDLEA) / 12.0)); + float div2 = rate * 2.0f / freq; + noteLookup->div2 = (int)div2; + + if (noteLookup->div2 == 0) + noteLookup->div2 = 1; + + 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); + + // Create the pitch tables + if (noteLookup->wavTable == NULL) + noteLookup->wavTable = new Bit32u[synth->controlROMMap->pcmCount]; + double rateMult = 32000.0 / rate; + double tuner = freq * 65536.0f; + for (int pc = 0; pc < synth->controlROMMap->pcmCount; pc++) { + noteLookup->wavTable[pc] = (int)(tuner / pcmWaves[pc].tune * rateMult); + } + + initFiltTable(noteLookup, freq, rate); + initNFiltTable(noteLookup, freq, rate); + return file; +} + +bool Tables::initNotes(Synth *synth, PCMWaveEntry *pcmWaves, float rate, float masterTune) { + const char *NoteNames[12] = { + "C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B " + }; + char filename[64]; + int intRate = (int)rate; + char version[4] = {0, 0, 0, 5}; + sprintf(filename, "waveformcache-%d-%.2f.raw", intRate, masterTune); + + File *file = NULL; + char header[20]; + strncpy(header, "MT32WAVE", 8); + int pos = 8; + // Version... + for (int i = 0; i < 4; i++) + header[pos++] = version[i]; + header[pos++] = (char)((intRate >> 24) & 0xFF); + header[pos++] = (char)((intRate >> 16) & 0xFF); + header[pos++] = (char)((intRate >> 8) & 0xFF); + header[pos++] = (char)(intRate & 0xFF); + int intTuning = (int)masterTune; + header[pos++] = (char)((intTuning >> 8) & 0xFF); + header[pos++] = (char)(intTuning & 0xFF); + header[pos++] = 0; + header[pos] = (char)((masterTune - intTuning) * 10); +#if MT32EMU_WAVECACHEMODE < 2 + bool reading = false; + file = synth->openFile(filename, File::OpenMode_read); + if (file != NULL) { + char fileHeader[20]; + if (file->read(fileHeader, 20) == 20) { + if (memcmp(fileHeader, header, 20) == 0) { + Bit16u endianCheck; + if (file->readBit16u(&endianCheck)) { + if (endianCheck == 1) { + reading = true; + } else { + synth->printDebug("Endian check in %s does not match expected", filename); + } + } else { + synth->printDebug("Unable to read endian check in %s", filename); + } + } else { + synth->printDebug("Header of %s does not match expected", filename); + } + } else { + synth->printDebug("Error reading 16 bytes of %s", filename); + } + if (!reading) { + file->close(); + file = NULL; + } + } else { + synth->printDebug("Unable to open %s for reading", filename); + } +#endif + + float progress = 0.0f; + bool abort = false; + synth->report(ReportType_progressInit, &progress); + for (int f = LOWEST_NOTE; f <= HIGHEST_NOTE; f++) { + synth->printDebug("Initialising note %s%d", NoteNames[f % 12], (f / 12) - 2); + NoteLookup *noteLookup = ¬eLookups[f - LOWEST_NOTE]; + file = initNote(synth, noteLookup, (float)f, rate, masterTune, pcmWaves, file); + progress = (f - LOWEST_NOTE + 1) / (float)NUM_NOTES; + abort = synth->report(ReportType_progressInit, &progress) != 0; + if (abort) + break; + } + +#if MT32EMU_WAVECACHEMODE == 0 || MT32EMU_WAVECACHEMODE == 2 + if (file == NULL) { + file = synth->openFile(filename, File::OpenMode_write); + if (file != NULL) { + if (file->write(header, 20) == 20 && file->writeBit16u(1)) { + for (int f = 0; f < NUM_NOTES; f++) { + for (int i = 0; i < 3 && file != NULL; i++) { + int len = noteLookups[f].waveformSize[i]; + for (int j = 0; j < len; j++) { + if (!file->writeBit16u(noteLookups[f].waveforms[i][j])) { + synth->printDebug("Error writing waveform cache file"); + file->close(); + file = NULL; + break; + } + } + } + } + } else { + synth->printDebug("Error writing 16-byte header to %s - won't continue saving", filename); + } + } else { + synth->printDebug("Unable to open %s for writing - won't be created", filename); + } + } +#endif + + if (file != NULL) + synth->closeFile(file); + return !abort; +} + +void Tables::freeNotes() { + for (int t = 0; t < 3; t++) { + for (int m = 0; m < NUM_NOTES; m++) { + if (noteLookups[m].waveforms[t] != NULL) { + delete[] noteLookups[m].waveforms[t]; + noteLookups[m].waveforms[t] = NULL; + noteLookups[m].waveformSize[t] = 0; + } + if (noteLookups[m].wavTable != NULL) { + delete[] noteLookups[m].wavTable; + noteLookups[m].wavTable = NULL; + } + } + } + initialisedMasterTune = 0.0f; +} + +Tables::Tables() { + initialisedSampleRate = 0.0f; + initialisedMasterTune = 0.0f; + memset(¬eLookups, 0, sizeof(noteLookups)); +} + +bool Tables::init(Synth *synth, PCMWaveEntry *pcmWaves, float sampleRate, float masterTune) { + if (sampleRate <= 0.0f) { + synth->printDebug("Bad sampleRate (%d <= 0.0f)", sampleRate); + return false; + } + if (initialisedSampleRate == 0.0f) { + initMT32ConstantTables(synth); + } + if (initialisedSampleRate != sampleRate) { + initFiltCoeff(sampleRate); + initEnvelopes(sampleRate); + for (int key = 12; key <= 108; key++) { + initDep(&keyLookups[key - 12], (float)key); + } + } + if (initialisedSampleRate != sampleRate || initialisedMasterTune != masterTune) { + freeNotes(); + if (!initNotes(synth, pcmWaves, sampleRate, masterTune)) { + return false; + } + initialisedSampleRate = sampleRate; + initialisedMasterTune = masterTune; + } + return true; +} + +} diff --git a/engines/sci/sfx/softseq/mt32/tables.h b/engines/sci/sfx/softseq/mt32/tables.h new file mode 100644 index 0000000000..d9af5114b2 --- /dev/null +++ b/engines/sci/sfx/softseq/mt32/tables.h @@ -0,0 +1,116 @@ +/* Copyright (c) 2003-2005 Various contributors + * + * 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. + */ + +#ifndef MT32EMU_TABLES_H +#define MT32EMU_TABLES_H + +namespace MT32Emu { + +// Mathematical constants +const double DOUBLE_PI = 3.1415926535897932384626433832795; +const double DOUBLE_LN = 2.3025850929940456840179914546844; +const float FLOAT_PI = 3.1415926535897932384626433832795f; +const float FLOAT_LN = 2.3025850929940456840179914546844f; + +// Filter settings +const int FILTERGRAN = 512; + +// Amplitude of waveform generator +// FIXME: This value is the amplitude possible whilst avoiding +// overdriven values immediately after filtering when playing +// back SQ3MT.MID. Needs to be checked. +const int WGAMP = 12382; + +const int MIDDLEC = 60; +const int MIDDLEA = 69; // By this I mean "A above middle C" + +// FIXME:KG: may only need to do 12 to 108 +// 12..108 is the range allowed by note on commands, but the key can be modified by pitch keyfollow +// and adjustment for timbre pitch, so the results can be outside that range. +// Should we move it (by octave) into the 12..108 range, or keep it in 0..127 range, +// or something else altogether? +const int LOWEST_NOTE = 12; +const int HIGHEST_NOTE = 127; +const int NUM_NOTES = HIGHEST_NOTE - LOWEST_NOTE + 1; // Number of slots for note LUT + +class Synth; + +struct NoteLookup { + Bit32u div2; + Bit32u *wavTable; + Bit32s sawTable[101]; + int filtTable[2][201]; + int nfiltTable[101][101]; + Bit16s *waveforms[3]; + Bit32u waveformSize[3]; +}; + +struct KeyLookup { + Bit32s envTimeMult[5]; // For envelope time adjustment for key pressed + Bit32s envDepthMult[5]; +}; + +class Tables { + float initialisedSampleRate; + float initialisedMasterTune; + void initMT32ConstantTables(Synth *synth); + static Bit16s clampWF(Synth *synth, const char *n, float ampVal, double input); + static File *initWave(Synth *synth, NoteLookup *noteLookup, float ampsize, float div2, File *file); + bool initNotes(Synth *synth, PCMWaveEntry *pcmWaves, float rate, float tuning); + void initEnvelopes(float sampleRate); + void initFiltCoeff(float samplerate); +public: + // Constant LUTs + Bit32s tvfKeyfollowMult[217]; + Bit32s tvfVelfollowMult[128][101]; + Bit32s tvfBiasMult[15][128]; + Bit32u tvaVelfollowMult[128][101]; + Bit32s tvaBiasMult[13][128]; + Bit16s noiseBuf[MAX_SAMPLE_OUTPUT]; + Bit16s sintable[65536]; + Bit32s pitchEnvVal[16][101]; + Bit32s envTimeVelfollowMult[5][128]; + Bit32s pwVelfollowAdd[15][128]; + float resonanceFactor[31]; + Bit32u lfoShift[101][101]; + Bit32s pwFactor[101]; + Bit32s volumeMult[101]; + + // LUTs varying with sample rate + Bit32u envTime[101]; + Bit32u envDeltaMaxTime[101]; + Bit32u envDecayTime[101]; + Bit32u lfoPeriod[101]; + float filtCoeff[FILTERGRAN][31][8]; + + // Various LUTs for each note and key + NoteLookup noteLookups[NUM_NOTES]; + KeyLookup keyLookups[97]; + + Tables(); + bool init(Synth *synth, PCMWaveEntry *pcmWaves, float sampleRate, float masterTune); + File *initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float tuning, PCMWaveEntry *pcmWaves, File *file); + void freeNotes(); +}; + +} + +#endif diff --git a/engines/sci/sfx/softseq/opl2.c b/engines/sci/sfx/softseq/opl2.c new file mode 100644 index 0000000000..ed2f3001c8 --- /dev/null +++ b/engines/sci/sfx/softseq/opl2.c @@ -0,0 +1,718 @@ +/*************************************************************************** + opl2.c Copyright (C) 2002/04 Solomon Peachy, Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + +***************************************************************************/ + +#include +#include +#include "../softseq.h" +#include "../adlib.h" +#include + +#include "fmopl.h" + +#ifdef _DREAMCAST +#define SAMPLE_RATE 22050 +#define CHANNELS SFX_PCM_MONO +#define STEREO 0 +#else +#define SAMPLE_RATE 44100 +#define CHANNELS SFX_PCM_STEREO_LR +#define STEREO 1 +#endif + + +/* local function declarations */ + +static void opl2_allstop(sfx_softseq_t *self); + +//#define DEBUG_ADLIB + +/* portions shamelessly lifted from claudio's XMP */ +/* other portions lifted from sound/opl3.c in the Linux kernel */ + +#define ADLIB_LEFT 0 +#define ADLIB_RIGHT 1 + +/* #define OPL_INTERNAL_FREQ 3600000 */ +#define OPL_INTERNAL_FREQ 3579545 + +static int ready = 0; +static int pcmout_stereo = STEREO; + +static int register_base[11] = { + 0x20, 0x23, 0x40, 0x43, + 0x60, 0x63, 0x80, 0x83, + 0xe0, 0xe3, 0xc0 +}; + +static int register_offset[12] = { + /* Channel 1 2 3 4 5 6 7 8 9 */ + /* Operator 1 */ 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12, 0x18, 0x19, 0x1A + +}; + +static int ym3812_note[13] = { + 0x157, 0x16b, 0x181, 0x198, 0x1b0, 0x1ca, + 0x1e5, 0x202, 0x220, 0x241, 0x263, 0x287, + 0x2ae +}; + +static guint8 sci_adlib_vol_base[16] = { + 0x00, 0x11, 0x15, 0x19, 0x1D, 0x22, 0x26, 0x2A, + 0x2E, 0x23, 0x37, 0x3B, 0x3F, 0x3F, 0x3F, 0x3F +}; +static guint8 sci_adlib_vol_tables[16][64]; + +/* ripped out of the linux kernel, of all places. */ +static gint8 fm_volume_table[128] = { + -64, -48, -40, -35, -32, -29, -27, -26, + -24, -23, -21, -20, -19, -18, -18, -17, + -16, -15, -15, -14, -13, -13, -12, -12, + -11, -11, -10, -10, -10, -9, -9, -8, + -8, -8, -7, -7, -7, -6, -6, -6, + -5, -5, -5, -5, -4, -4, -4, -4, + -3, -3, -3, -3, -2, -2, -2, -2, + -2, -1, -1, -1, -1, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 4, + 4, 4, 4, 4, 4, 4, 4, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 8 +}; + +/* back to your regularly scheduled definitions */ + +static guint8 instr[MIDI_CHANNELS]; +static guint16 pitch[MIDI_CHANNELS]; +static guint8 vol[MIDI_CHANNELS]; +static guint8 pan[MIDI_CHANNELS]; +static int free_voices = ADLIB_VOICES; +static guint8 oper_note[ADLIB_VOICES]; +static guint8 oper_chn[ADLIB_VOICES]; + +static FM_OPL *ym3812_L = NULL; +static FM_OPL *ym3812_R = NULL; + +static guint8 adlib_reg_L[256]; +static guint8 adlib_reg_R[256]; +static guint8 adlib_master; + + +/* initialise note/operator lists, etc. */ +void adlibemu_init_lists(void) +{ + int i; + + int j; + + for (i = 0 ; i < 16 ; i++) { + for (j = 0; j < 64 ; j++) { + sci_adlib_vol_tables[i][j] = ((guint16)sci_adlib_vol_base[i]) * j / 63; + } + } + + for (i = 0; i < MIDI_CHANNELS ; i++) { + pitch[i] = 8192; /* center the pitch wheel */ + } + + free_voices = ADLIB_VOICES; + + memset(instr, 0, sizeof(instr)); + memset(vol, 0x7f, sizeof(vol)); + memset(pan, 0x3f, sizeof(pan)); + memset(adlib_reg_L, 0, sizeof(adlib_reg_L)); + memset(adlib_reg_R, 0, sizeof(adlib_reg_R)); + memset(oper_chn, 0xff, sizeof(oper_chn)); + memset(oper_note, 0xff, sizeof(oper_note)); + adlib_master=12; +} + +/* more shamelessly lifted from xmp and adplug. And altered. :) */ + +static inline int opl_write_L (int a, int v) +{ + adlib_reg_L[a] = v; + OPLWrite (ym3812_L, 0x388, a); + return OPLWrite (ym3812_L, 0x389, v); +} + +static inline int opl_write_R (int a, int v) +{ + adlib_reg_R[a] = v; + OPLWrite (ym3812_R, 0x388, a); + return OPLWrite (ym3812_R, 0x389, v); +} + +static inline int opl_write(int a, int v) +{ + opl_write_L(a,v); + return opl_write_R(a,v); +} + +/* +static inline guint8 opl_read (int a) +{ + OPLWrite (ym3812_L, 0x388, a); + return OPLRead (ym3812_L, 0x389); +} +*/ + +void synth_setpatch (int voice, guint8 *data) +{ + int i; + + opl_write(0xBD, 0); + + for (i = 0; i < 10; i++) + opl_write(register_base[i] + register_offset[voice], data[i]); + + opl_write(register_base[10] + voice, data[10]); + + /* mute voice after patch change */ + opl_write_L(0xb0 + voice, adlib_reg_L[0xb0+voice] & 0xdf); + opl_write_R(0xb0 + voice, adlib_reg_R[0xb0+voice] & 0xdf); + +#ifdef DEBUG_ADLIB + for (i = 0; i < 10; i++) + printf("%02x ", adlib_reg_L[register_base[i]+register_offset[voice]]); + printf("%02x ", adlib_reg_L[register_base[10]+voice]); +#endif + +} + +void synth_setvolume_L (int voice, int volume) +{ + gint8 level1, level2; + + level1 = ~adlib_reg_L[register_base[2]+register_offset[voice]] & 0x3f; + level2 = ~adlib_reg_L[register_base[3]+register_offset[voice]] & 0x3f; + + if (level1) { + level1 += sci_adlib_vol_tables[adlib_master][volume>>1]; + } + + if (level2) { + level2 += sci_adlib_vol_tables[adlib_master][volume>>1]; + } + + if (level1 > 0x3f) + level1 = 0x3f; + if (level1 < 0) + level1 = 0; + + if (level2 > 0x3f) + level2 = 0x3f; + if (level2 < 0) + level2 = 0; + + /* algorithm-dependent; we may need to set both operators. */ + if (adlib_reg_L[register_base[10]+voice] & 1) + opl_write_L(register_base[2]+register_offset[voice], + (guint8)((~level1 &0x3f) | + (adlib_reg_L[register_base[2]+register_offset[voice]]&0xc0))); + + opl_write_L(register_base[3]+register_offset[voice], + (guint8)((~level2 &0x3f) | + (adlib_reg_L[register_base[3]+register_offset[voice]]&0xc0))); + +} + +void synth_setvolume_R (int voice, int volume) +{ + gint8 level1, level2; + + level1 = ~adlib_reg_R[register_base[2]+register_offset[voice]] & 0x3f; + level2 = ~adlib_reg_R[register_base[3]+register_offset[voice]] & 0x3f; + + if (level1) { + level1 += sci_adlib_vol_tables[adlib_master][volume>>1]; + } + + if (level2) { + level2 += sci_adlib_vol_tables[adlib_master][volume>>1]; + } + + if (level1 > 0x3f) + level1 = 0x3f; + if (level1 < 0) + level1 = 0; + + if (level2 > 0x3f) + level2 = 0x3f; + if (level2 < 0) + level2 = 0; + + /* now for the other side. */ + if (adlib_reg_R[register_base[10]+voice] & 1) + opl_write_R(register_base[2]+register_offset[voice], + (guint8)((~level1 &0x3f) | + (adlib_reg_R[register_base[2]+register_offset[voice]]&0xc0))); + + opl_write_R(register_base[3]+register_offset[voice], + (guint8)((~level2 &0x3f) | + (adlib_reg_R[register_base[3]+register_offset[voice]]&0xc0))); + +} + +void synth_setnote (int voice, int note, int bend) +{ + int n, fre, oct; + float delta; + + delta = 0; + + n = note % 12; + + if (bend < 8192) + bend = 8192-bend; + delta = pow(2, (float) (bend%8192)/8192.0); + + if (bend > 8192) + fre = ym3812_note[n]*delta; else + fre = ym3812_note[n]/delta; + + oct = note / 12 - 1; + + if (oct < 0) + oct = 0; + + opl_write(0xa0 + voice, fre & 0xff); + opl_write(0xb0 + voice, + 0x20 | ((oct << 2) & 0x1c) | ((fre >> 8) & 0x03)); +#ifdef DEBUG_ADLIB + printf("-- %02x %02x\n", adlib_reg_L[0xa0+voice], adlib_reg_L[0xb0+voice]); +#endif + +} + + +/* back to your regularly scheduled driver */ + +int adlibemu_stop_note(int chn, int note, int velocity) +{ + int i, op=255; + + // sciprintf("Note off %d %d %d\n", chn, note, velocity); + + for (i=0;i 0x3f) /* pan right; so we scale the left down. */ + volume_L = volume_L / 0x3f * (0x3f - (pan[chn] - 0x3f)); + else if (pan[chn] < 0x3f) /* pan left; so we scale the right down.*/ + volume_R = volume_R / 0x3f * (0x3f - (0x3f-pan[chn])); + } + inst = instr[chn]; + + synth_setpatch(op, adlib_sbi[inst]); + synth_setvolume_L(op, volume_L); + synth_setvolume_R(op, volume_R); + synth_setnote(op, note, pitch[chn]); + + oper_chn[op] = chn; + oper_note[op] = note; + free_voices--; + +#ifdef DEBUG_ADLIB + printf("play voice %d (%d rem): C%02x N%02x V%02x/%02x-%02x P%02x (%02x/%02x)\n", op, free_voices, chn, note, velocity, volume_L, volume_R, inst, + adlib_reg_L[register_base[2]+register_offset[op]] & 0x3f, + adlib_reg_L[register_base[3]+register_offset[op]] & 0x3f); +#endif + + return 0; +} + +static +void adlibemu_update_pitch(int chn, int note, int newpitch) +{ + int i; + int matched = 0; + + pitch[chn] = newpitch; + + for (i=0;i 0) { + samples = remaining_delta * pcmout_sample_rate / 1000000; + samples = sci_min(samples, remaining); + if (samples) { + YM3812UpdateOne(ADLIB_LEFT, ptr, samples, 1); + YM3812UpdateOne(ADLIB_RIGHT, ptr+1, samples, 1); + } + if (remaining > samples) { + remaining_delta = (remaining - samples) * 1000000 / pcmout_sample_rate; + } else { + song->play_next_note(); + remaining_delta = song->get_next_delta(); + song->advance(); + } + remaining -= samples; + } + } +#endif + + if (pcmout_stereo) { + YM3812UpdateOne (ym3812_L, ptr, count, 1); + YM3812UpdateOne (ym3812_R, ptr+1, count, 1); + } else { + YM3812UpdateOne (ym3812_L, ptr, count, 0); + } +} + +static int +opl2_init(sfx_softseq_t *self, byte *data_ptr, int data_length, byte *data2_ptr, + int data2_length) +{ + int i; + + /* load up the patch.003 file, parse out the instruments */ + if (data_length < 1344) { + sciprintf ("[sfx:seq:opl2] Invalid patch.003: Expected %d, got %d\n", 1344, data_length); + return -1; + } + + for (i = 0; i < 48; i++) + make_sbi((adlib_def *)(data_ptr+(28 * i)), adlib_sbi[i]); + + if (data_length > 1344) + for (i = 48; i < 96; i++) + make_sbi((adlib_def *)(data_ptr+2+(28 * i)), adlib_sbi[i]); + + OPLBuildTables(FMOPL_ENV_BITS_HQ, FMOPL_EG_ENT_HQ); + + if (!(ym3812_L = OPLCreate (OPL_TYPE_YM3812, OPL_INTERNAL_FREQ, SAMPLE_RATE)) || + !(ym3812_R = OPLCreate (OPL_TYPE_YM3812, OPL_INTERNAL_FREQ, SAMPLE_RATE))) { + sciprintf ("[sfx:seq:opl2] Failure: Emulator init failed!\n"); + return SFX_ERROR; + } + + ready = 1; + + opl2_allstop(self); + return SFX_OK; +} + + +static void +opl2_exit(sfx_softseq_t *self) +{ + FM_OPL *opl = ym3812_L; + ym3812_L = NULL; + OPLDestroy(opl); + opl = ym3812_R; + ym3812_R = NULL; + OPLDestroy(opl); + + // XXX deregister with pcm layer. +} + +static void +opl2_allstop(sfx_softseq_t *self) +{ + // printf("AdlibEmu: Reset\n"); + if (! ready) + return; + + adlibemu_init_lists(); + + OPLResetChip (ym3812_L); + OPLResetChip (ym3812_R); + + opl_write(0x01, 0x20); + opl_write(0xBD, 0xc0); + +#ifdef DEBUG_ADLIB + printf("ADLIB: reset complete\n"); +#endif + // test_adlib(); +} + +int midi_adlibemu_reverb(short param) +{ + printf("ADLIB: reverb NYI %04x \n", param); + return 0; +} + +int midi_adlibemu_event(guint8 command, guint8 note, guint8 velocity, guint32 delta) +{ + guint8 channel, oper; + + channel = command & 0x0f; + oper = command & 0xf0; + + switch (oper) { + case 0x80: + return adlibemu_stop_note(channel, note, velocity); + case 0x90: /* noteon and noteoff */ + return adlibemu_start_note(channel, note, velocity); + case 0xe0: /* Pitch bend */ + { + int bend = (note & 0x7f) | ((velocity & 0x7f) << 7); +// printf("Bend to %d\n", bend); + adlibemu_update_pitch(channel, note, bend); + } + case 0xb0: /* CC changes. */ + switch (note) { + case 0x07: + vol[channel] = velocity; + break; + case 0x0a: + pan[channel] = velocity; + break; + case 0x4b: + break; + case 0x7b: { /* all notes off */ + int i = 0; + for (i=0;i + +***************************************************************************/ +/* PC speaker software sequencer for FreeSCI */ + +#include "../softseq.h" +#include + +#define FREQUENCY 94020 + +static int volume = 0x0600; +static int note = 0; /* Current halftone, or 0 if off */ +static int freq_count = 0; + +extern sfx_softseq_t sfx_softseq_pcspeaker; +/* Forward-declare the sequencer we are defining here */ + + +static int +sps_set_option(sfx_softseq_t *self, char *name, char *value) +{ + return SFX_ERROR; +} + +static int +sps_init(sfx_softseq_t *self, byte *patch, int patch_len, byte *patch2, + int patch2_len) +{ + return SFX_OK; +} + +static void +sps_exit(sfx_softseq_t *self) +{ +} + +static void +sps_event(sfx_softseq_t *self, byte command, int argc, byte *argv) +{ +#if 0 + fprintf(stderr, "Note [%02x : %02x %02x]\n", command, argc?argv[0] : 0, (argc > 1)? argv[1] : 0); +#endif + + switch (command & 0xf0) { + + case 0x80: + if (argv[0] == note) + note = 0; + break; + + case 0x90: + if (!argv[1]) { + if (argv[0] == note) + note = 0; + } else + note = argv[0]; + /* Ignore velocity otherwise; we just use the global one */ + break; + + case 0xb0: + if (argv[1] == SCI_MIDI_CHANNEL_NOTES_OFF) + note = 0; + break; + + default: +#if DEBUG + fprintf(stderr, "[SFX:PCM-PC] Unused MIDI command %02x %02x %02x\n", command, argc?argv[0] : 0, (argc > 1)? argv[1] : 0); +#endif + break; /* ignore */ + } +} + +#define BASE_NOTE 129 /* A10 */ +#define BASE_OCTAVE 10 /* A10, as I said */ + +static int +freq_table[12] = { /* A4 is 440Hz, halftone map is x |-> ** 2^(x/12) */ + 28160, /* A10 */ + 29834, + 31608, + 33488, + 35479, + 37589, + 39824, + 42192, + 44701, + 47359, + 50175, + 53159 +}; + + +void +sps_poll(sfx_softseq_t *self, byte *dest, int len) +{ + int halftone_delta = note - BASE_NOTE; + int oct_diff = ((halftone_delta + BASE_OCTAVE * 12) / 12) - BASE_OCTAVE; + int halftone_index = (halftone_delta + (12*100)) % 12 ; + int freq = (!note)? 0 : freq_table[halftone_index] / (1 << (-oct_diff)); + gint16 *buf = (gint16 *) dest; + + int i; + for (i = 0; i < len; i++) { + if (note) { + freq_count += freq; + while (freq_count >= (FREQUENCY << 1)) + freq_count -= (FREQUENCY << 1); + + if (freq_count - freq < 0) { + /* Unclean rising edge */ + int l = volume << 1; + buf[i] = -volume + (l*freq_count)/freq; + } else if (freq_count >= FREQUENCY + && freq_count - freq < FREQUENCY) { + /* Unclean falling edge */ + int l = volume << 1; + buf[i] = volume - (l*(freq_count - FREQUENCY))/freq; + } else { + if (freq_count < FREQUENCY) + buf[i] = volume; + else + buf[i] = -volume; + } + } else + buf[i] = 0; + } + +} + +void +sps_volume(sfx_softseq_t *self, int new_volume) +{ + volume = new_volume << 4; +} + +void +sps_allstop(sfx_softseq_t *self) +{ + note = 0; +} + +sfx_softseq_t sfx_softseq_pcspeaker = { + "pc-speaker", + "0.3", + sps_set_option, + sps_init, + sps_exit, + sps_volume, + sps_event, + sps_poll, + sps_allstop, + NULL, + SFX_SEQ_PATCHFILE_NONE, + SFX_SEQ_PATCHFILE_NONE, + 0x20, /* PC speaker channel only */ + 0, /* No rhythm channel */ + 1, /* # of voices */ + {FREQUENCY, SFX_PCM_MONO, SFX_PCM_FORMAT_S16_NATIVE} +}; diff --git a/engines/sci/sfx/softseq/softsequencers.c b/engines/sci/sfx/softseq/softsequencers.c new file mode 100644 index 0000000000..d0f544190e --- /dev/null +++ b/engines/sci/sfx/softseq/softsequencers.c @@ -0,0 +1,71 @@ +/*************************************************************************** + softsequencers.c Copyright (C) 2004 Christoph Reichenbach + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + + Please contact the maintainer for any program-related bug reports or + inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "../softseq.h" + +extern sfx_softseq_t sfx_softseq_opl2; +extern sfx_softseq_t sfx_softseq_SN76496; +extern sfx_softseq_t sfx_softseq_pcspeaker; +extern sfx_softseq_t sfx_softseq_amiga; +extern sfx_softseq_t sfx_softseq_mt32; +extern sfx_softseq_t sfx_softseq_fluidsynth; + +static sfx_softseq_t *sw_sequencers[] = { + &sfx_softseq_opl2, +/* &sfx_softseq_mt32, */ + &sfx_softseq_SN76496, + &sfx_softseq_pcspeaker, + &sfx_softseq_amiga, +#ifdef HAVE_FLUIDSYNTH_H + &sfx_softseq_fluidsynth, +#endif + NULL +}; + + +sfx_softseq_t * +sfx_find_softseq(char *name) +{ + if (!name) + return sw_sequencers[0]; + else { + int i = 0; + while (sw_sequencers[i]) + if (!strcmp(sw_sequencers[i]->name, name)) + return sw_sequencers[i]; + else + ++i; + + return NULL; /* Not found */ + } +} diff --git a/engines/sci/sfx/songlib.c b/engines/sci/sfx/songlib.c new file mode 100644 index 0000000000..36287845f0 --- /dev/null +++ b/engines/sci/sfx/songlib.c @@ -0,0 +1,282 @@ +/*************************************************************************** + songlib.c Copyright (C) 2002 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) + +***************************************************************************/ + +#include +#include +#include + +#define debug_stream stderr + +GTimeVal +song_sleep_time(GTimeVal *lastslept, long ticks) +{ + GTimeVal tv; + long timetosleep; + long timeslept; /* Time already slept */ + timetosleep = ticks * SOUND_TICK; /* Time to sleep in us */ + + sci_get_current_time(&tv); + timeslept = 1000000 * (tv.tv_sec - lastslept->tv_sec) + + tv.tv_usec - lastslept->tv_usec; + + timetosleep -= timeslept; + + if (timetosleep < 0) + timetosleep = 0; + + tv.tv_sec = timetosleep / 1000000; + tv.tv_usec = timetosleep % 1000000; + + return tv; +} + + +GTimeVal +song_next_wakeup_time(GTimeVal *lastslept, long ticks) +{ + GTimeVal retval; + + retval.tv_sec = lastslept->tv_sec + (ticks / 60); + retval.tv_usec = lastslept->tv_usec + ((ticks % 60) * SOUND_TICK); + + if (retval.tv_usec >= 1000000) { + retval.tv_usec -= 1000000; + ++retval.tv_sec; + } + + return retval; +} + + +song_t * +song_new(song_handle_t handle, song_iterator_t *it, int priority) +{ + song_t *retval; + retval = (song_t*) sci_malloc(sizeof(song_t)); + +#ifdef SATISFY_PURIFY + memset(retval, 0, sizeof(song_t)); +#endif + + retval->handle = handle; + retval->priority = priority; + retval->next = NULL; + retval->delay = 0; + retval->it = it; + retval->status = SOUND_STATUS_STOPPED; + retval->next_playing = NULL; + retval->next_stopping = NULL; + retval->restore_behavior = RESTORE_BEHAVIOR_CONTINUE; + retval->restore_time = 0; + + return retval; +} + + +void +song_lib_add(songlib_t songlib, song_t *song) +{ + song_t **seeker = NULL; + int pri = song->priority; + + if (NULL == song) { + sciprintf("song_lib_add(): NULL passed for song\n"); + return; + } + + if (*(songlib.lib) == NULL) { + *(songlib.lib) = song; + song->next = NULL; + + return; + } + + seeker = (songlib.lib); + while (*seeker && ((*seeker)->priority > pri)) + seeker = &((*seeker)->next); + + song->next = *seeker; + *seeker = song; +} + +static void /* Recursively free a chain of songs */ +_songfree_chain(song_t *song) +{ + if (song) { + _songfree_chain(song->next); + songit_free(song->it); + song->it = NULL; + free(song); + } +} + + +void +song_lib_init(songlib_t *songlib) +{ + songlib->lib = &(songlib->_s); + songlib->_s = NULL; +} + +void +song_lib_free(songlib_t songlib) +{ + _songfree_chain(*(songlib.lib)); + *(songlib.lib) = NULL; +} + + +song_t * +song_lib_find(songlib_t songlib, song_handle_t handle) +{ + song_t *seeker = *(songlib.lib); + + while (seeker) { + if (seeker->handle == handle) + break; + seeker = seeker->next; + } + + return seeker; +} + + +song_t * +song_lib_find_next_active(songlib_t songlib, song_t *other) +{ + song_t *seeker = other? other->next : *(songlib.lib); + + while (seeker) { + if ((seeker->status == SOUND_STATUS_WAITING) || + (seeker->status == SOUND_STATUS_PLAYING)) + break; + seeker = seeker->next; + } + + /* Only return songs that have equal priority */ + if (other && seeker && other->priority > seeker->priority) + return NULL; + + return seeker; +} + +song_t * +song_lib_find_active(songlib_t songlib) +{ + return song_lib_find_next_active(songlib, NULL); +} + +int +song_lib_remove(songlib_t songlib, song_handle_t handle) +{ + int retval; + song_t *goner = *(songlib.lib); + + if (!goner) + return -1; + + if (goner->handle == handle) + *(songlib.lib) = goner->next; + + else { + while ((goner->next) && (goner->next->handle != handle)) + goner = goner->next; + + if (goner->next) { /* Found him? */ + song_t *oldnext = goner->next; + + goner->next = goner->next->next; + goner = oldnext; + } else return -1; /* No. */ + } + + retval = goner->status; + + songit_free(goner->it); + free(goner); + + return retval; +} + +void +song_lib_resort(songlib_t songlib, song_t *song) +{ + if (*(songlib.lib) == song) + *(songlib.lib) = song->next; + else { + song_t *seeker = *(songlib.lib); + + while (seeker->next && (seeker->next != song)) + seeker = seeker->next; + + if (seeker->next) + seeker->next = seeker->next->next; + } + + song_lib_add(songlib, song); +} + +int +song_lib_count(songlib_t songlib) +{ + song_t *seeker = *(songlib.lib); + int retval = 0; + + while (seeker) { + retval++; + seeker = seeker->next; + } + + return retval; +} + +void +song_lib_set_restore_behavior(songlib_t songlib, song_handle_t handle, RESTORE_BEHAVIOR action) +{ + song_t *seeker = song_lib_find(songlib, handle); + + seeker->restore_behavior = action; +} + +void +song_lib_dump(songlib_t songlib, int line) +{ + song_t *seeker = *(songlib.lib); + + fprintf(debug_stream,"L%d:", line); + do { + fprintf(debug_stream," %p", (void *)seeker); + + if (seeker) { + fprintf(debug_stream,"[%04lx,p=%d,s=%d]->", seeker->handle, seeker->priority,seeker->status); + seeker = seeker->next; + } + fprintf(debug_stream,"\n"); + } while (seeker); + fprintf(debug_stream,"\n"); + +} + diff --git a/engines/sci/sfx/test-iterator.c b/engines/sci/sfx/test-iterator.c new file mode 100644 index 0000000000..1ecce673f9 --- /dev/null +++ b/engines/sci/sfx/test-iterator.c @@ -0,0 +1,450 @@ +/*************************************************************************** + Copyright (C) 2008 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantability, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include "sfx_iterator_internal.h" +#include +#include + +#define ASSERT_S(x) if (!(x)) { error("Failed assertion in L%d: " #x "\n", __LINE__); return; } +#define ASSERT(x) ASSERT_S(x) + +/* Tests the song iterators */ + +int errors = 0; + +void +error(char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "[ERROR] "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + ++errors; +} + + +/* The simple iterator will finish after a fixed amount of time. Before that, +** it emits (absolute) cues in ascending order. */ +struct simple_it_struct { + INHERITS_SONG_ITERATOR; + int lifetime_remaining; + char *cues; + int cue_counter; + int cue_progress; + int cues_nr; +} simple_iterator; + +int +simple_it_next(song_iterator_t *_self, unsigned char *buf, int *result) +{ + struct simple_it_struct *self = (struct simple_it_struct *) _self; + + if (self->lifetime_remaining == -1) { + error("Song iterator called post mortem"); + return SI_FINISHED; + } + + if (self->lifetime_remaining) { + + if (self->cue_counter < self->cues_nr) { + int time_to_cue = self->cues[self->cue_counter]; + + if (self->cue_progress == time_to_cue) { + ++self->cue_counter; + self->cue_progress = 0; + *result = self->cue_counter; + return SI_ABSOLUTE_CUE; + } else { + int retval = time_to_cue - self->cue_progress; + self->cue_progress = time_to_cue; + + if (retval > self->lifetime_remaining) { + retval = self->lifetime_remaining; + self->lifetime_remaining = 0; + self->cue_progress = retval; + return retval; + } + + self->lifetime_remaining -= retval; + return retval; + } + } else { + int retval = self->lifetime_remaining; + self->lifetime_remaining = 0; + return retval; + } + + } else { + self->lifetime_remaining = -1; + return SI_FINISHED; + } +} + +sfx_pcm_feed_t * +simple_it_pcm_feed(song_iterator_t *_self) +{ + error("No PCM feed!\n"); + return NULL; +} + +void +simple_it_init(song_iterator_t *_self) +{ +} + +song_iterator_t * +simple_it_handle_message(song_iterator_t *_self, song_iterator_message_t msg) +{ + return NULL; +} + +void +simple_it_cleanup(song_iterator_t *_self) +{ +} + +/* Initialises the simple iterator. +** Parameters: (int) delay: Number of ticks until the iterator finishes +** (int *) cues: An array of cue delays (cue values are [1,2...]) +** (int) cues_nr: Number of cues in ``cues'' +** The first cue is emitted after cues[0] ticks, and it is 1. After cues[1] additional ticks +** the next cue is emitted, and so on. */ +song_iterator_t * +setup_simple_iterator(int delay, char *cues, int cues_nr) +{ + simple_iterator.lifetime_remaining = delay; + simple_iterator.cues = cues; + simple_iterator.cue_counter = 0; + simple_iterator.cues_nr = cues_nr; + simple_iterator.cue_progress = 0; + + simple_iterator.ID = 42; + simple_iterator.channel_mask = 0x004f; + simple_iterator.flags = 0; + simple_iterator.priority = 1; + + simple_iterator.death_listeners_nr = 0; + + simple_iterator.cleanup = simple_it_cleanup; + simple_iterator.init = simple_it_init; + simple_iterator.handle_message = simple_it_handle_message; + simple_iterator.get_pcm_feed = simple_it_pcm_feed; + simple_iterator.next = simple_it_next; + + return (song_iterator_t *) &simple_iterator; +} + +#define ASSERT_SIT ASSERT(it == simple_it) +#define ASSERT_FFIT ASSERT(it == ff_it) +#define ASSERT_NEXT(n) ASSERT(songit_next(&it, data, &result, IT_READER_MASK_ALL) == n) +#define ASSERT_RESULT(n) ASSERT(result == n) +#define ASSERT_CUE(n) ASSERT_NEXT(SI_ABSOLUTE_CUE); ASSERT_RESULT(n) + +void +test_simple_it() +{ + song_iterator_t *it; + song_iterator_t *simple_it = (song_iterator_t *) &simple_iterator; + unsigned char data[4]; + int result; + puts("[TEST] simple iterator (test artifact)"); + + it = setup_simple_iterator(42, NULL, 0); + + ASSERT_SIT; + ASSERT_NEXT(42); + ASSERT_SIT; + ASSERT_NEXT(SI_FINISHED); + ASSERT_SIT; + + it = setup_simple_iterator(42, "\003\004", 2); + ASSERT_SIT; + ASSERT_NEXT(3); + ASSERT_CUE(1); + ASSERT_SIT; + ASSERT_NEXT(4); + ASSERT_CUE(2); + ASSERT_SIT; +// fprintf(stderr, "XXX => %d\n", songit_next(&it, data, &result, IT_READER_MASK_ALL)); + ASSERT_NEXT(35); + ASSERT_NEXT(SI_FINISHED); + ASSERT_SIT; + + puts("[TEST] Test OK."); +} + +void +test_fastforward() +{ + song_iterator_t *it; + song_iterator_t *simple_it = (song_iterator_t *) &simple_iterator; + song_iterator_t *ff_it; + unsigned char data[4]; + int result; + puts("[TEST] fast-forward iterator"); + + it = setup_simple_iterator(42, NULL, 0); + ff_it = it = new_fast_forward_iterator(it, 0); + ASSERT_FFIT; + ASSERT_NEXT(42); + ASSERT_SIT; /* Must have morphed back */ + ASSERT_NEXT(SI_FINISHED); + ASSERT_SIT; + + it = setup_simple_iterator(42, NULL, 0); + ff_it = it = new_fast_forward_iterator(it, 1); + ASSERT_FFIT; + ASSERT_NEXT(41); + /* May or may not have morphed back here */ + ASSERT_NEXT(SI_FINISHED); + ASSERT_SIT; + + it = setup_simple_iterator(42, NULL, 0); + ff_it = it = new_fast_forward_iterator(it, 41); + ASSERT_FFIT; + ASSERT_NEXT(1); + /* May or may not have morphed back here */ + ASSERT_NEXT(SI_FINISHED); + ASSERT_SIT; + + it = setup_simple_iterator(42, NULL, 0); + ff_it = it = new_fast_forward_iterator(it, 42); + ASSERT_NEXT(SI_FINISHED); + /* May or may not have morphed back here */ + + it = setup_simple_iterator(42, NULL, 0); + ff_it = it = new_fast_forward_iterator(it, 10000); + ASSERT_NEXT(SI_FINISHED); + /* May or may not have morphed back here */ + + it = setup_simple_iterator(42, "\003\004", 2); + ff_it = it = new_fast_forward_iterator(it, 2); + ASSERT_FFIT; + ASSERT_NEXT(1); + ASSERT_CUE(1); + ASSERT_SIT; + ASSERT_NEXT(4); + ASSERT_CUE(2); + ASSERT_SIT; + ASSERT_NEXT(35); + ASSERT_NEXT(SI_FINISHED); + ASSERT_SIT; + + it = setup_simple_iterator(42, "\003\004", 2); + ff_it = it = new_fast_forward_iterator(it, 5); + ASSERT_FFIT; + ASSERT_CUE(1); + ASSERT_FFIT; + ASSERT_NEXT(2); + ASSERT_CUE(2); + ASSERT_SIT; + ASSERT_NEXT(35); + ASSERT_NEXT(SI_FINISHED); + ASSERT_SIT; + + it = setup_simple_iterator(42, "\003\004", 2); + ff_it = it = new_fast_forward_iterator(it, 41); + ASSERT_FFIT; + ASSERT_CUE(1); + ASSERT_FFIT; + ASSERT_CUE(2); + ASSERT_FFIT; + ASSERT_NEXT(1); + ASSERT_NEXT(SI_FINISHED); + ASSERT_SIT; + + puts("[TEST] Test OK."); +} + +#define SIMPLE_SONG_SIZE 50 + +static unsigned char simple_song[SIMPLE_SONG_SIZE] = { + 0x00, /* Regular song */ + /* Only use channel 0 for all devices */ + 0x02, 0xff, 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, + /* Song begins here */ + 42, 0x90, 60, 0x7f, /* Play C after 42 ticks */ + 02, 64, 0x42, /* Play E after 2 more ticks, using running status mode */ + 0xf8, 10, 0x80, 60, 0x02, /* Stop C after 250 ticks */ + 0, 64, 0x00, /* Stop E immediately */ + 00, 0xfc /* Stop song */ +}; + +#define ASSERT_MIDI3(cmd, arg0, arg1) \ + ASSERT(data[0] == cmd); \ + ASSERT(data[1] == arg0); \ + ASSERT(data[2] == arg1); + +void +test_iterator_sci0() +{ + song_iterator_t *it = songit_new (simple_song, SIMPLE_SONG_SIZE, SCI_SONG_ITERATOR_TYPE_SCI0, 0l); + unsigned char data[4]; + int result; + SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x0001)); /* Initialise song, enabling channel 0 */ + + puts("[TEST] SCI0-style song"); + ASSERT_NEXT(42); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 60, 0x7f); + ASSERT_NEXT(2); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 64, 0x42); + ASSERT_NEXT(250); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 60, 0x02); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 64, 0x00); + ASSERT_NEXT(SI_FINISHED); + puts("[TEST] Test OK."); +} + + + +void +test_iterator_sci0_loop() +{ + song_iterator_t *it = songit_new (simple_song, SIMPLE_SONG_SIZE, SCI_SONG_ITERATOR_TYPE_SCI0, 0l); + unsigned char data[4]; + int result; + SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x0001)); /* Initialise song, enabling channel 0 */ + SIMSG_SEND(it, SIMSG_SET_LOOPS(2)); /* Loop one additional time */ + + puts("[TEST] SCI0-style song with looping"); + ASSERT_NEXT(42); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 60, 0x7f); + ASSERT_NEXT(2); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 64, 0x42); + ASSERT_NEXT(250); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 60, 0x02); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 64, 0x00); + ASSERT_NEXT(SI_LOOP); + ASSERT_NEXT(42); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 60, 0x7f); + ASSERT_NEXT(2); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 64, 0x42); + ASSERT_NEXT(250); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 60, 0x02); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 64, 0x00); + ASSERT_NEXT(SI_FINISHED); + puts("[TEST] Test OK."); +} + + + +#define LOOP_SONG_SIZE 54 + +unsigned char loop_song[LOOP_SONG_SIZE] = { + 0x00, /* Regular song song */ + /* Only use channel 0 for all devices */ + 0x02, 0xff, 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, + /* Song begins here */ + 42, 0x90, 60, 0x7f, /* Play C after 42 ticks */ + 13, 0x80, 60, 0x00, /* Stop C after 13 ticks */ + 00, 0xCF, 0x7f, /* Set loop point */ + 02, 0x90, 64, 0x42, /* Play E after 2 more ticks, using running status mode */ + 03, 0x80, 64, 0x00, /* Stop E after 3 ticks */ + 00, 0xfc /* Stop song/loop */ +}; + + +void +test_iterator_sci0_mark_loop() +{ + song_iterator_t *it = songit_new (loop_song, LOOP_SONG_SIZE, SCI_SONG_ITERATOR_TYPE_SCI0, 0l); + unsigned char data[4]; + int result; + SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x0001)); /* Initialise song, enabling channel 0 */ + SIMSG_SEND(it, SIMSG_SET_LOOPS(3)); /* Loop once more */ + + puts("[TEST] SCI0-style song with loop mark, looping"); + ASSERT_NEXT(42); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 60, 0x7f); + ASSERT_NEXT(13); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 60, 0x00); + /* Loop point here: we don't observe that in the iterator interface yet, though */ + ASSERT_NEXT(2); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 64, 0x42); + ASSERT_NEXT(3); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 64, 0x00); + /* Now we loop back to the loop pont */ + ASSERT_NEXT(SI_LOOP); + ASSERT_NEXT(2); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 64, 0x42); + ASSERT_NEXT(3); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 64, 0x00); + /* ...and one final time */ + ASSERT_NEXT(SI_LOOP); + ASSERT_NEXT(2); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 64, 0x42); + ASSERT_NEXT(3); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 64, 0x00); + + ASSERT_NEXT(SI_FINISHED); + puts("[TEST] Test OK."); +} + + + +int +main(int argc, char **argv) +{ + test_simple_it(); + test_fastforward(); + test_iterator_sci0(); + test_iterator_sci0_loop(); + test_iterator_sci0_mark_loop(); + if (errors != 0) + fprintf(stderr, "[ERROR] %d errors total.\n", errors); + return (errors != 0); +} diff --git a/engines/sci/sfx/tests/stubs.c b/engines/sci/sfx/tests/stubs.c new file mode 100644 index 0000000000..06da6f9419 --- /dev/null +++ b/engines/sci/sfx/tests/stubs.c @@ -0,0 +1,9 @@ +int sciprintf(char *fmt,...) +{ + return 0; +} + +int sfx_pcm_available() +{ + return 1; +} \ No newline at end of file diff --git a/engines/sci/sfx/tests/tests.cpp b/engines/sci/sfx/tests/tests.cpp new file mode 100644 index 0000000000..10eacc0de2 --- /dev/null +++ b/engines/sci/sfx/tests/tests.cpp @@ -0,0 +1,172 @@ +#include +#include + +#define TESTEQUAL(x, y) if(x != y) printf("test failure: expected %04x, got %04x @ file %s line %d \n", x, y, __FILE__, __LINE__); + +static char calledDeathListenerCallback; + +static unsigned char song[] = { +// PCM not present +0, +// channel defs +0, 0x20, 0, 0x21, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +// note on, wait, note off +0xAA, +0x90, 0xAA, 0xAA, +0xAA, +0x80, 0xAA, 0xAA, +0xAA, +0x91, 0xAA, 0xAA, +0xAA, +0x81, 0xAA, 0xAA, +0xAA, +// end track +0xFC}; + +#define SONG_CMD_COUNT 10 + +#define TEST_SETUP() \ + unsigned char cmds[4] = {0}; \ + int result = 0; \ + int message; \ + int i; \ +\ + song_iterator_t *it = songit_new(song, sizeof(song), 0, 0); \ + it->init(it); \ + SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x20));\ + calledDeathListenerCallback = 0; + +#define TEST_TEARDOWN() \ + songit_free(it); + + +void testFinishSong() +{ + TEST_SETUP(); + message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL); + TESTEQUAL(0xAA, message); + + message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL); + TESTEQUAL(0, message); + TESTEQUAL(3, result); + + for (i=0; i < SONG_CMD_COUNT - 2; i++) + { + message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL); + } + + TESTEQUAL(SI_FINISHED, message); + + TEST_TEARDOWN(); +} + + +void DeathListenerCallback(void *v1, void *v2) +{ + calledDeathListenerCallback++; + return; +} + +void testDeathListener() +{ + TEST_SETUP(); + + song_iterator_add_death_listener( + it, + it, + (void (*)(void *, void*))DeathListenerCallback); + + for (i=0; i < SONG_CMD_COUNT; i++) + { + message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL); + } + + TESTEQUAL(SI_FINISHED, message); + + TEST_TEARDOWN(); + + TESTEQUAL(1, calledDeathListenerCallback); +} + +void testMultipleDeathListeners() +{ + TEST_SETUP(); + + song_iterator_add_death_listener( + it, + it, + (void (*)(void *, void*))DeathListenerCallback); + + song_iterator_add_death_listener( + it, + it, + (void (*)(void *, void*))DeathListenerCallback); + + for (i=0; i < SONG_CMD_COUNT; i++) + { + message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL); + } + + TESTEQUAL(SI_FINISHED, message); + + TEST_TEARDOWN(); + + TESTEQUAL(2, calledDeathListenerCallback); +} + +void testStopSong() +{ + TEST_SETUP(); + SIMSG_SEND(it, SIMSG_STOP); + + message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL); + TESTEQUAL(SI_FINISHED, message); + + TEST_TEARDOWN(); +} + +void testStopLoopedSong() +{ + TEST_SETUP(); + + SIMSG_SEND(it, SIMSG_SET_LOOPS(3)); + message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL); + TESTEQUAL(0xAA, message); + + SIMSG_SEND(it, SIMSG_STOP); + message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL); + TESTEQUAL(SI_FINISHED, message); + + TEST_TEARDOWN(); +} + +void testChangeSongMask() +{ + TEST_SETUP(); + + message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL); + TESTEQUAL(0xAA, message); + + SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x40)); + message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL); + TESTEQUAL(0, message); + TESTEQUAL(0, result); + + TEST_TEARDOWN(); +} + + +int main(int argc, char* argv[]) +{ + testFinishSong(); + testDeathListener(); + testMultipleDeathListeners(); + testStopSong(); + testStopLoopedSong(); + testChangeSongMask(); + return 0; +} + diff --git a/engines/sci/sfx/tests/tests.vcproj b/engines/sci/sfx/tests/tests.vcproj new file mode 100644 index 0000000000..a50c794bce --- /dev/null +++ b/engines/sci/sfx/tests/tests.vcproj @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engines/sci/sfx/time.c b/engines/sci/sfx/time.c new file mode 100644 index 0000000000..553eb406f8 --- /dev/null +++ b/engines/sci/sfx/time.c @@ -0,0 +1,131 @@ +/*************************************************************************** + time.c Copyright (C) 2003 Christoph Reichenbach + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + + Please contact the maintainer for any program-related bug reports or + inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include + +sfx_timestamp_t +sfx_new_timestamp(long secs, long usecs, int frame_rate) +{ + sfx_timestamp_t r; + r.secs = secs; + r.usecs = usecs; + r.frame_rate = frame_rate; + r.frame_offset = 0; + + return r; +} + + +sfx_timestamp_t +sfx_timestamp_add(sfx_timestamp_t timestamp, int frames) +{ + timestamp.frame_offset += frames; + + if (timestamp.frame_offset < 0) { + int secsub = 1 + (-timestamp.frame_offset / timestamp.frame_rate); + + timestamp.frame_offset += timestamp.frame_rate * secsub; + timestamp.secs -= secsub; + } + + timestamp.secs += (timestamp.frame_offset / timestamp.frame_rate); + timestamp.frame_offset %= timestamp.frame_rate; + + return timestamp; +} + +int +sfx_timestamp_frame_diff(sfx_timestamp_t a, sfx_timestamp_t b) +{ + long usecdelta = 0; + + if (a.frame_rate != b.frame_rate) { + fprintf(stderr, "Fatal: The semantics of subtracting two timestamps with a different base from each other is not defined!\n"); + BREAKPOINT(); + } + + if (a.usecs != b.usecs) { +#if (SIZEOF_LONG >= 8) + usecdelta = (a.usecs * a.frame_rate) / 1000000 + - (b.usecs * b.frame_rate) / 1000000; +#else + usecdelta = ((a.usecs/1000) * a.frame_rate) / 1000 + - ((b.usecs/1000) * b.frame_rate) / 1000; +#endif + } + + return usecdelta + + (a.secs - b.secs) * a.frame_rate + + a.frame_offset - b.frame_offset; +} + +long +sfx_timestamp_usecs_diff(sfx_timestamp_t t1, sfx_timestamp_t t2) +{ + long secs1, secs2; + long usecs1, usecs2; + + sfx_timestamp_gettime(&t1, &secs1, &usecs1); + sfx_timestamp_gettime(&t2, &secs2, &usecs2); + + return (usecs1 - usecs2) + ((secs1 - secs2) * 1000000); +} + +sfx_timestamp_t +sfx_timestamp_renormalise(sfx_timestamp_t timestamp, int new_freq) +{ + sfx_timestamp_t r; + sfx_timestamp_gettime(×tamp, &r.secs, &r.usecs); + r.frame_rate = new_freq; + r.frame_offset = 0; + + return r; +} + +void +sfx_timestamp_gettime(sfx_timestamp_t *timestamp, long *secs, long *usecs) +{ + long ust = timestamp->usecs; + /* On 64 bit machines, we can do an accurate computation */ +#if (SIZEOF_LONG >= 8) + ust += (timestamp->frame_offset * 1000000l) / (timestamp->frame_rate); +#else + ust += (timestamp->frame_offset * 1000l) / (timestamp->frame_rate / 1000l); +#endif + + if (ust > 1000000) { + ust -= 1000000; + *secs = timestamp->secs + 1; + } else + *secs = timestamp->secs; + + *usecs = ust; +} + diff --git a/engines/sci/sfx/timer/Makefile.am b/engines/sci/sfx/timer/Makefile.am new file mode 100644 index 0000000000..74f3bee426 --- /dev/null +++ b/engines/sci/sfx/timer/Makefile.am @@ -0,0 +1,4 @@ +EXTRA_DIST = pthread.c +noinst_LIBRARIES = libscitimer.a +INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@ +libscitimer_a_SOURCES = timers.c sigalrm.c diff --git a/engines/sci/sfx/timer/pthread.c b/engines/sci/sfx/timer/pthread.c new file mode 100644 index 0000000000..074adbe08f --- /dev/null +++ b/engines/sci/sfx/timer/pthread.c @@ -0,0 +1,104 @@ +/*************************************************************************** + pthread.c Copyright (C) 2005 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +#include +#include +#include + +/* Timer frequency in hertz */ +#define FREQ 60 + +/* Delay in ms */ +#define DELAY (1000 / FREQ) + +static void (*callback)(void *) = NULL; +static void *callback_data = NULL; +pthread_t thread; +volatile static int thread_run; + +static void * +timer_thread(void *arg) +{ + while (thread_run) { + if (callback) + callback(callback_data); + + usleep(DELAY * 1000); + } + + return NULL; +} + +static int +set_option(char *name, char *value) +{ + return SFX_ERROR; +} + +static int +init(void (*func)(void *), void *data) +{ + if (callback) { + fprintf(stderr, "Error: Attempt to initialize pthread timer more than once\n"); + return SFX_ERROR; + } + + if (!func) { + fprintf(stderr, "Error: Attempt to initialize pthread timer w/o callback\n"); + return SFX_ERROR; + } + + callback = func; + callback_data = data; + + thread_run = 1; + if (pthread_create(&thread, NULL, timer_thread, NULL)) { + fprintf(stderr, "Error: Could not create thread.\n"); + return SFX_ERROR; + } + + return SFX_OK; +} + +static int +stop(void) +{ + thread_run = 0; + pthread_join(thread, NULL); + + return SFX_OK; +} + +sfx_timer_t sfx_timer_pthread = { + "pthread", + "0.1", + DELAY, + 0, + &set_option, + &init, + &stop +}; diff --git a/engines/sci/sfx/timer/scummvm.cpp b/engines/sci/sfx/timer/scummvm.cpp new file mode 100644 index 0000000000..f4e08d441a --- /dev/null +++ b/engines/sci/sfx/timer/scummvm.cpp @@ -0,0 +1,52 @@ +#include "common/timer.h" +#include "engines/engine.h" +#include "sfx_timer.h" + + +#define FREQ 60 +#define DELAY (1000000 / FREQ) + +typedef void (*scummvm_timer_callback_t)(void *); +static scummvm_timer_callback_t scummvm_timer_callback = NULL; +static void *scummvm_timer_callback_data = NULL; +extern ::Engine *g_engine; + +void scummvm_timer_update_internal(void *ptr) { + if (scummvm_timer_callback) + scummvm_timer_callback(scummvm_timer_callback_data); +} + +int scummvm_timer_start(void (*func)(void *), void *data) { + if (scummvm_timer_callback) { + fprintf(stderr, + "Error: Attempt to initialize gametick timer more than once\n"); + return SFX_ERROR; + } + + if (!func) { + fprintf(stderr, + "Error: Attempt to initialize gametick timer w/o callback\n"); + return SFX_ERROR; + } + + scummvm_timer_callback = func; + scummvm_timer_callback_data = data; + + ::g_engine->_timer->installTimerProc(&scummvm_timer_update_internal, DELAY, NULL); + return SFX_OK; +} + +int scummvm_timer_stop() { + scummvm_timer_callback = NULL; + return SFX_OK; +} + + +sfx_timer_t sfx_timer_scummvm = { + "ScummVM", + "0.1", + DELAY/1000, 0, + NULL, + &scummvm_timer_start, + &scummvm_timer_stop + }; diff --git a/engines/sci/sfx/timer/sigalrm.c b/engines/sci/sfx/timer/sigalrm.c new file mode 100644 index 0000000000..40cc2872e1 --- /dev/null +++ b/engines/sci/sfx/timer/sigalrm.c @@ -0,0 +1,157 @@ +/*************************************************************************** + sigalrm.c Copyright (C) 2002 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include + +#ifdef HAVE_SETITIMER + +#include +#include +#include + +#ifdef HAVE_PTHREAD +#include +#define sigprocmask pthread_sigmask +#endif + +static void (*sig_callback)(void *) = NULL; +static void *sig_callback_data = NULL; +static sigset_t current_sigset; + +static void +timer_handler(int i) +{ + if (sig_callback) + sig_callback(sig_callback_data); +} + +static int +sigalrm_set_option(char *name, char *value) +{ + return SFX_ERROR; +} + + +static int +sigalrm_start(void) +{ + struct itimerval itimer; + + itimer.it_value.tv_sec = 0; + itimer.it_value.tv_usec = 1000000/60; + itimer.it_interval = itimer.it_value; + + signal(SIGALRM, timer_handler); /* Re-instate timer handler, to make sure */ + setitimer(ITIMER_REAL, &itimer, NULL); + + return SFX_OK; +} + + +static int +sigalrm_init(void (*callback)(void *), void *data) +{ + if (sig_callback) { + fprintf(stderr, "Error: Attempt to initialize sigalrm timer more than once\n"); + return SFX_ERROR; + } + + if (!callback) { + fprintf(stderr, "Error: Attempt to initialize sigalrm timer w/o callback\n"); + return SFX_ERROR; + } + + sig_callback = callback; + sig_callback_data = data; + + sigalrm_start(); + + sigemptyset(¤t_sigset); + sigaddset(¤t_sigset, SIGALRM); + + return SFX_OK; +} + + +static int +sigalrm_stop(void) +{ + struct itimerval itimer; + + if (!sig_callback) { + fprintf(stderr, "Error: Attempt to stop sigalrm timer when not running\n"); + return SFX_ERROR; + } + + itimer.it_value.tv_sec = 0; + itimer.it_value.tv_usec = 0; + itimer.it_interval = itimer.it_value; + + setitimer(ITIMER_REAL, &itimer, NULL); /* Stop timer */ + signal(SIGALRM, SIG_DFL); + + return SFX_OK; +} + + +static int +sigalrm_block(void) +{ + if (sigprocmask(SIG_BLOCK, ¤t_sigset, NULL) != 0) { + fprintf(stderr, "Error: Failed to block sigalrm\n"); + return SFX_ERROR; + } + + return SFX_OK; +} + + +static int +sigalrm_unblock(void) +{ + if (sigprocmask(SIG_UNBLOCK, ¤t_sigset, NULL) != 0) { + fprintf(stderr, "Error: Failed to unblock sigalrm\n"); + return SFX_ERROR; + } + + return SFX_OK; +} + + +sfx_timer_t sfx_timer_sigalrm = { + "sigalrm", + "0.1", + 17, /* 1000 / 60 */ + 0, + &sigalrm_set_option, + &sigalrm_init, + &sigalrm_stop, + &sigalrm_block, + &sigalrm_unblock +}; + +#endif /* HAVE_SETITIMER */ diff --git a/engines/sci/sfx/timer/timers.c b/engines/sci/sfx/timer/timers.c new file mode 100644 index 0000000000..b2a51d6062 --- /dev/null +++ b/engines/sci/sfx/timer/timers.c @@ -0,0 +1,73 @@ +/*************************************************************************** + timers.c Copyright (C) 2002 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include + +#ifdef SCUMMVM +extern sfx_timer_t sfx_timer_scummvm; +#else // SCUMMVM + +#ifdef HAVE_SETITIMER +extern sfx_timer_t sfx_timer_sigalrm; +#endif + +#ifdef _DREAMCAST +extern sfx_timer_t sfx_timer_pthread; +#endif +#endif // SCUMMVM + +sfx_timer_t *sfx_timers[] = { +#ifdef SCUMMVM + &sfx_timer_scummvm, +#else // SCUMMVM +#ifdef HAVE_SETITIMER + &sfx_timer_sigalrm, +#endif +#ifdef _DREAMCAST + &sfx_timer_pthread, +#endif +#endif // SCUMMVM + NULL +}; + + +sfx_timer_t * +sfx_find_timer(char *name) +{ + if (!name) { + /* Policies go here */ + return sfx_timers[0]; + } else { + int n = 0; + while (sfx_timers[n] + && strcasecmp(sfx_timers[n]->name, name)) + ++n; + + return sfx_timers[n]; + } +} diff --git a/engines/sci/sfx/timetest.c b/engines/sci/sfx/timetest.c new file mode 100644 index 0000000000..80d9ad1b78 --- /dev/null +++ b/engines/sci/sfx/timetest.c @@ -0,0 +1,59 @@ +/*************************************************************************** + timetest.c Copyright (C) 2003 Christoph Reichenbach + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + + Please contact the maintainer for any program-related bug reports or + inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include +#include + +sfx_timestamp_t a,b,c; + +int +main(int argc, char **argv) +{ + int i; + a = sfx_new_timestamp(10, 0, 1000); + b = sfx_new_timestamp(10, 1000, 1000); + c = sfx_new_timestamp(10, 2000, 1000); + + assert(sfx_timestamp_sample_diff(a, b) == -1); + assert(sfx_timestamp_sample_diff(b, a) == 1); + assert(sfx_timestamp_sample_diff(c, a) == 2); + assert(sfx_timestamp_sample_diff(sfx_timestamp_add(b, 2000), a) == 2001); + assert(sfx_timestamp_sample_diff(sfx_timestamp_add(b, 2000), sfx_timestamp_add(a, -1000)) == 3001); + + for (i = -10000; i < 10000; i++) { + int v = sfx_timestamp_sample_diff(sfx_timestamp_add(c, i), c); + if (v != i) { + fprintf(stderr, "After adding %d samples: Got diff of %d\n", i, v); + return 1; + } + } + + return 0; +} diff --git a/engines/sci/tools/5x8.font b/engines/sci/tools/5x8.font new file mode 100644 index 0000000000..53fc58c70d --- /dev/null +++ b/engines/sci/tools/5x8.font @@ -0,0 +1,2307 @@ +# 256 8 +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x00 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x01 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x02 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x03 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x04 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x05 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x06 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x07 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x08 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x09 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x0a ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x0b ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x0c ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x0d ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x0e ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x0f ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x10 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x11 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x12 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x13 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x14 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x15 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x16 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x17 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x18 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x19 ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x1a ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x1b ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x1c ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x1d ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x1e ('.') */ +..... +#.#.. +...#. +#.... +...#. +#.... +.#.#. +..... +---- 0x1f ('.') */ +..... +..... +..... +..... +..... +..... +..... +..... +---- 0x20 (' ') */ +.. +#. +#. +#. +#. +.. +#. +.. +---- 0x21 ('!') */ +.... +#.#. +#.#. +.... +.... +.... +.... +.... +---- 0x22 ('"') */ +.#.#.. +.#.#.. +#####. +.#.#.. +#####. +.#.#.. +.#.#.. +...... +---- 0x23 ('#') */ +..#... +.###.. +#.#... +.###.. +..#.#. +.###.. +..#... +...... +---- 0x24 ('$') */ +...... +##..#. +#..#.. +..#... +.#..#. +#..##. +...... +...... +---- 0x25 ('%') */ +.#.... +#.#... +#.#... +.#.... +#.###. +#..#.. +.##.#. +...... +---- 0x26 ('&') */ +.. +#. +#. +.. +.. +.. +.. +.. +---- 0x27 (''') */ +... +.#. +#.. +#.. +#.. +#.. +.#. +... +---- 0x28 ('(') */ +... +#.. +.#. +.#. +.#. +.#. +#.. +... +---- 0x29 (')') */ +..... +..... +#..#. +.##.. +####. +.##.. +#..#. +..... +---- 0x2a ('*') */ +...... +...... +..#... +..#... +#####. +..#... +..#... +...... +---- 0x2b ('+') */ +... +... +... +... +... +.#. +.#. +#.. +---- 0x2c (',') */ +..... +..... +..... +..... +####. +..... +..... +..... +---- 0x2d ('-') */ +.. +.. +.. +.. +.. +.. +#. +.. +---- 0x2e ('.') */ +....... +.....#. +....#.. +...#... +..#.... +.#..... +#...... +....... +---- 0x2f ('/') */ +..... +.##.. +#..#. +#..#. +#..#. +#..#. +.##.. +..... +---- 0x30 ('0') */ +.... +.#.. +##.. +.#.. +.#.. +.#.. +###. +.... +---- 0x31 ('1') */ +..... +.##.. +#..#. +...#. +.##.. +#.... +####. +..... +---- 0x32 ('2') */ +..... +####. +..#.. +.##.. +...#. +#..#. +.##.. +..... +---- 0x33 ('3') */ +..... +..#.. +.##.. +#.#.. +####. +..#.. +..#.. +..... +---- 0x34 ('4') */ +..... +####. +#.... +###.. +...#. +#..#. +.##.. +..... +---- 0x35 ('5') */ +..... +.##.. +#.... +###.. +#..#. +#..#. +.##.. +..... +---- 0x36 ('6') */ +..... +####. +...#. +..#.. +..#.. +.#... +.#... +..... +---- 0x37 ('7') */ +..... +.##.. +#..#. +.##.. +#..#. +#..#. +.##.. +..... +---- 0x38 ('8') */ +..... +.##.. +#..#. +#..#. +.###. +...#. +.##.. +..... +---- 0x39 ('9') */ +.. +.. +#. +.. +.. +#. +.. +.. +---- 0x3a (':') */ +... +... +.#. +... +... +.#. +.#. +#.. +---- 0x3b (';') */ +.... +..#. +.#.. +#... +#... +.#.. +..#. +.... +---- 0x3c ('<') */ +..... +..... +..... +####. +..... +####. +..... +..... +---- 0x3d ('=') */ +.... +#... +.#.. +..#. +..#. +.#.. +#... +.... +---- 0x3e ('>') */ +..... +.##.. +#..#. +...#. +..#.. +..... +..#.. +..... +---- 0x3f ('?') */ +..##.. +.#..#. +#..##. +#.#.#. +#.#.#. +#..#.. +.#.... +..##.. +---- 0x40 ('@') */ +..... +.##.. +#..#. +#..#. +####. +#..#. +#..#. +..... +---- 0x41 ('A') */ +..... +###.. +#..#. +###.. +#..#. +#..#. +###.. +..... +---- 0x42 ('B') */ +..... +.##.. +#..#. +#.... +#.... +#..#. +.##.. +..... +---- 0x43 ('C') */ +..... +###.. +#..#. +#..#. +#..#. +#..#. +###.. +..... +---- 0x44 ('D') */ +..... +####. +#.... +###.. +#.... +#.... +####. +..... +---- 0x45 ('E') */ +..... +####. +#.... +###.. +#.... +#.... +#.... +..... +---- 0x46 ('F') */ +..... +.##.. +#..#. +#.... +#.##. +#..#. +.##.. +..... +---- 0x47 ('G') */ +..... +#..#. +#..#. +####. +#..#. +#..#. +#..#. +..... +---- 0x48 ('H') */ +.... +###. +.#.. +.#.. +.#.. +.#.. +###. +.... +---- 0x49 ('I') */ +..... +.###. +..#.. +..#.. +..#.. +#.#.. +.#... +..... +---- 0x4a ('J') */ +..... +#..#. +#.#.. +##... +#.#.. +#.#.. +#..#. +..... +---- 0x4b ('K') */ +..... +#.... +#.... +#.... +#.... +#.... +####. +..... +---- 0x4c ('L') */ +...... +#...#. +##.##. +#.#.#. +#...#. +#...#. +#...#. +...... +---- 0x4d ('M') */ +..... +#..#. +##.#. +##.#. +#.##. +#.##. +#..#. +..... +---- 0x4e ('N') */ +..... +.##.. +#..#. +#..#. +#..#. +#..#. +.##.. +..... +---- 0x4f ('O') */ +..... +###.. +#..#. +#..#. +###.. +#.... +#.... +..... +---- 0x50 ('P') */ +..... +.##.. +#..#. +#..#. +#..#. +#.##. +.##.. +...#. +---- 0x51 ('Q') */ +..... +###.. +#..#. +#..#. +###.. +#..#. +#..#. +..... +---- 0x52 ('R') */ +..... +.##.. +#..#. +.#... +..#.. +#..#. +.##.. +..... +---- 0x53 ('S') */ +.... +###. +.#.. +.#.. +.#.. +.#.. +.#.. +.... +---- 0x54 ('T') */ +..... +#..#. +#..#. +#..#. +#..#. +#..#. +.##.. +..... +---- 0x55 ('U') */ +..... +#..#. +#..#. +#..#. +#..#. +#.#.. +.#... +..... +---- 0x56 ('V') */ +...... +#...#. +#...#. +#...#. +#.#.#. +##.##. +#...#. +...... +---- 0x57 ('W') */ +..... +#..#. +#..#. +.##.. +.##.. +#..#. +#..#. +..... +---- 0x58 ('X') */ +...... +#...#. +#...#. +.#.#.. +..#... +..#... +..#... +...... +---- 0x59 ('Y') */ +..... +####. +...#. +..#.. +.#... +#.... +####. +..... +---- 0x5a ('Z') */ +.... +###. +#... +#... +#... +#... +###. +.... +---- 0x5b ('[') */ +....... +#...... +.#..... +..#.... +...#... +....#.. +.....#. +....... +---- 0x5c ('\') */ +.... +###. +..#. +..#. +..#. +..#. +###. +.... +---- 0x5d (']') */ +.... +.#.. +#.#. +.... +.... +.... +.... +.... +---- 0x5e ('^') */ +.... +.... +.... +.... +.... +.... +.... +#### +---- 0x5f ('_') */ +... +#.. +.#. +... +... +... +... +... +---- 0x60 ('`') */ +..... +..... +..... +.###. +#..#. +#..#. +.###. +..... +---- 0x61 ('a') */ +..... +#.... +#.... +###.. +#..#. +#..#. +###.. +..... +---- 0x62 ('b') */ +.... +.... +.... +.##. +#... +#... +.##. +.... +---- 0x63 ('c') */ +..... +...#. +...#. +.###. +#..#. +#..#. +.###. +..... +---- 0x64 ('d') */ +..... +..... +..... +.##.. +#.##. +##... +.##.. +..... +---- 0x65 ('e') */ +..... +..#.. +.#.#. +.#... +###.. +.#... +.#... +..... +---- 0x66 ('f') */ +..... +..... +..... +.##.. +#..#. +.###. +...#. +.##.. +---- 0x67 ('g') */ +..... +#.... +#.... +###.. +#..#. +#..#. +#..#. +..... +---- 0x68 ('h') */ +.... +.#.. +.... +##.. +.#.. +.#.. +###. +.... +---- 0x69 ('i') */ +.... +..#. +.... +..#. +..#. +..#. +#.#. +.#.. +---- 0x6a ('j') */ +..... +#.... +#.... +#..#. +###.. +#..#. +#..#. +..... +---- 0x6b ('k') */ +.... +##.. +.#.. +.#.. +.#.. +.#.. +###. +.... +---- 0x6c ('l') */ +...... +...... +...... +##.#.. +#.#.#. +#.#.#. +#.#.#. +...... +---- 0x6d ('m') */ +..... +..... +..... +###.. +#..#. +#..#. +#..#. +..... +---- 0x6e ('n') */ +..... +..... +..... +.##.. +#..#. +#..#. +.##.. +..... +---- 0x6f ('o') */ +..... +..... +..... +###.. +#..#. +###.. +#.... +#.... +---- 0x70 ('p') */ +..... +..... +..... +.###. +#..#. +.###. +...#. +...#. +---- 0x71 ('q') */ +..... +..... +..... +#.#.. +##.#. +#.... +#.... +..... +---- 0x72 ('r') */ +.... +.... +.... +.##. +##.. +..#. +##.. +.... +---- 0x73 ('s') */ +..... +.#... +.#... +###.. +.#... +.#.#. +..#.. +..... +---- 0x74 ('t') */ +..... +..... +..... +#..#. +#..#. +#..#. +.###. +..... +---- 0x75 ('u') */ +.... +.... +.... +#.#. +#.#. +#.#. +.#.. +.... +---- 0x76 ('v') */ +...... +...... +...... +#...#. +#.#.#. +#.#.#. +.#.#.. +...... +---- 0x77 ('w') */ +..... +..... +..... +#..#. +.##.. +.##.. +#..#. +..... +---- 0x78 ('x') */ +..... +..... +..... +#..#. +#..#. +.###. +#..#. +.##.. +---- 0x79 ('y') */ +..... +..... +..... +####. +..#.. +.#... +####. +..... +---- 0x7a ('z') */ +..##. +.#... +..#.. +##... +..#.. +.#... +..##. +..... +---- 0x7b ('{') */ +.. +#. +#. +#. +#. +#. +#. +.. +---- 0x7c ('|') */ +##... +..#.. +.#... +..##. +.#... +..#.. +##... +..... +---- 0x7d ('}') */ +..... +.#.#. +#.#.. +..... +..... +..... +..... +..... +---- 0x7e ('~') */ +..... +..... +..... +..... +..... +..... +..... +..... +---- 0x7f ('.') */ +..... +..#.. +..... +..#.. +..#.. +..#.. +..#.. +..... +---- 0x80 ('.') */ +..... +..... +..#.. +.###. +#.#.. +#.#.. +.###. +..#.. +---- 0x81 ('.') */ +..... +..#.. +.#.#. +###.. +.#... +.#.#. +#.#.. +..... +---- 0x82 ('.') */ +..... +..... +#...# +.###. +.#.#. +.###. +#...# +..... +---- 0x83 ('.') */ +..... +#...# +.#.#. +##### +..#.. +##### +..#.. +..... +---- 0x84 ('.') */ +..#.. +..#.. +..#.. +..... +..#.. +..#.. +..#.. +..... +---- 0x85 ('.') */ +.###. +#.... +###.. +#..#. +.###. +...#. +###.. +..... +---- 0x86 ('.') */ +..... +.#.#. +..... +..... +..... +..... +..... +..... +---- 0x87 ('.') */ +..... +.###. +#.#.# +##..# +##..# +#.#.# +.###. +..... +---- 0x88 ('.') */ +..##. +.#.#. +..##. +..... +.###. +..... +..... +..... +---- 0x89 ('.') */ +..... +..... +..... +.#.#. +#.#.. +.#.#. +..... +..... +---- 0x8a ('.') */ +..... +..... +..... +..... +.###. +...#. +...#. +..... +---- 0x8b ('.') */ +..... +..... +..... +..... +.###. +..... +..... +..... +---- 0x8c ('.') */ +..... +.###. +###.# +##.## +###.# +##.## +.###. +..... +---- 0x8d ('.') */ +..... +.###. +..... +..... +..... +..... +..... +..... +---- 0x8e ('.') */ +..... +..#.. +.#.#. +..#.. +..... +..... +..... +..... +---- 0x8f ('.') */ +..... +..... +..#.. +.###. +..#.. +..... +.###. +..... +---- 0x90 ('.') */ +..#.. +.#.#. +...#. +..#.. +.###. +..... +..... +..... +---- 0x91 ('.') */ +.##.. +...#. +.##.. +...#. +.##.. +..... +..... +..... +---- 0x92 ('.') */ +..... +..#.. +.#... +..... +..... +..... +..... +..... +---- 0x93 ('.') */ +..... +..... +..... +#..#. +#..#. +#..#. +###.. +#.... +---- 0x94 ('.') */ +..... +.#### +###.# +###.# +.##.# +..#.# +..#.# +..... +---- 0x95 ('.') */ +..... +..... +..... +..... +..#.. +..... +..... +..... +---- 0x96 ('.') */ +..... +..... +..... +..... +..... +..... +..#.. +.#... +---- 0x97 ('.') */ +..#.. +.##.. +..#.. +..#.. +.###. +..... +..... +..... +---- 0x98 ('.') */ +..#.. +.#.#. +..#.. +..... +.###. +..... +..... +..... +---- 0x99 ('.') */ +..... +..... +..... +#.#.. +.#.#. +#.#.. +..... +..... +---- 0x9a ('.') */ +#.... +#.... +#.... +#.#.. +.##.. +####. +..#.. +..... +---- 0x9b ('.') */ +#.... +#.... +#.#.. +##.#. +...#. +..#.. +.###. +..... +---- 0x9c ('.') */ +#.... +.#... +#.... +.##.. +#.#.. +####. +..#.. +..... +---- 0x9d ('.') */ +..... +..#.. +..... +..#.. +.#... +.#.#. +..#.. +..... +---- 0x9e ('.') */ +.#... +..#.. +.##.. +#..#. +####. +#..#. +#..#. +..... +---- 0x9f ('.') */ +..#.. +.#... +.##.. +#..#. +####. +#..#. +#..#. +..... +---- 0xa0 ('.') */ +.##.. +#..#. +.##.. +#..#. +####. +#..#. +#..#. +..... +---- 0xa1 ('.') */ +.#.#. +#.#.. +.##.. +#..#. +####. +#..#. +#..#. +..... +---- 0xa2 ('.') */ +#..#. +..... +.##.. +#..#. +####. +#..#. +#..#. +..... +---- 0xa3 ('.') */ +.##.. +#..#. +.##.. +#..#. +####. +#..#. +#..#. +..... +---- 0xa4 ('.') */ +..... +.###. +#.#.. +#.#.. +####. +#.#.. +#.##. +..... +---- 0xa5 ('.') */ +..... +.##.. +#..#. +#.... +#.... +#..#. +.##.. +.#... +---- 0xa6 ('.') */ +.#... +..#.. +####. +#.... +###.. +#.... +####. +..... +---- 0xa7 ('.') */ +..#.. +.#... +####. +#.... +###.. +#.... +####. +..... +---- 0xa8 ('.') */ +.##.. +#..#. +####. +#.... +###.. +#.... +####. +..... +---- 0xa9 ('.') */ +#..#. +..... +####. +#.... +###.. +#.... +####. +..... +---- 0xaa ('.') */ +.#... +..#.. +.###. +..#.. +..#.. +..#.. +.###. +..... +---- 0xab ('.') */ +...#. +..#.. +.###. +..#.. +..#.. +..#.. +.###. +..... +---- 0xac ('.') */ +..#.. +.#.#. +.###. +..#.. +..#.. +..#.. +.###. +..... +---- 0xad ('.') */ +.#.#. +..... +.###. +..#.. +..#.. +..#.. +.###. +..... +---- 0xae ('.') */ +..... +.###. +.#..# +###.# +.#..# +.#..# +.###. +..... +---- 0xaf ('.') */ +.#.#. +#.#.. +#..#. +##.#. +#.##. +#..#. +#..#. +..... +---- 0xb0 ('.') */ +.#... +..#.. +.##.. +#..#. +#..#. +#..#. +.##.. +..... +---- 0xb1 ('.') */ +..#.. +.#... +.##.. +#..#. +#..#. +#..#. +.##.. +..... +---- 0xb2 ('.') */ +.##.. +#..#. +.##.. +#..#. +#..#. +#..#. +.##.. +..... +---- 0xb3 ('.') */ +.#.#. +#.#.. +.##.. +#..#. +#..#. +#..#. +.##.. +..... +---- 0xb4 ('.') */ +#..#. +..... +.##.. +#..#. +#..#. +#..#. +.##.. +..... +---- 0xb5 ('.') */ +..... +..... +..... +..... +.#.#. +..#.. +.#.#. +..... +---- 0xb6 ('.') */ +..... +.###. +#.##. +#.##. +##.#. +##.#. +###.. +..... +---- 0xb7 ('.') */ +.#... +..#.. +#..#. +#..#. +#..#. +#..#. +.##.. +..... +---- 0xb8 ('.') */ +..#.. +.#... +#..#. +#..#. +#..#. +#..#. +.##.. +..... +---- 0xb9 ('.') */ +.##.. +#..#. +#..#. +#..#. +#..#. +#..#. +.##.. +..... +---- 0xba ('.') */ +#..#. +..... +#..#. +#..#. +#..#. +#..#. +.##.. +..... +---- 0xbb ('.') */ +...#. +..#.. +#...# +.#.#. +..#.. +..#.. +..#.. +..... +---- 0xbc ('.') */ +..... +#.... +###.. +#..#. +#..#. +###.. +#.... +..... +---- 0xbd ('.') */ +..... +.##.. +#..#. +#.#.. +#.#.. +#..#. +#.#.. +..... +---- 0xbe ('.') */ +.#... +..#.. +..... +.###. +#..#. +#..#. +.###. +..... +---- 0xbf ('.') */ +..#.. +.#... +..... +.###. +#..#. +#..#. +.###. +..... +---- 0xc0 ('.') */ +..#.. +.#.#. +..... +.###. +#..#. +#..#. +.###. +..... +---- 0xc1 ('.') */ +.#.#. +#.#.. +..... +.###. +#..#. +#..#. +.###. +..... +---- 0xc2 ('.') */ +..... +.#.#. +..... +.###. +#..#. +#..#. +.###. +..... +---- 0xc3 ('.') */ +.##.. +#..#. +.##.. +.###. +#..#. +#..#. +.###. +..... +---- 0xc4 ('.') */ +..... +..... +..... +####. +.##.# +#.##. +.#### +..... +---- 0xc5 ('.') */ +..... +..... +..... +..##. +.#... +.#... +..##. +..#.. +---- 0xc6 ('.') */ +.#... +..#.. +..... +.##.. +#.##. +##... +.##.. +..... +---- 0xc7 ('.') */ +..#.. +.#... +..... +.##.. +#.##. +##... +.##.. +..... +---- 0xc8 ('.') */ +.##.. +#..#. +..... +.##.. +#.##. +##... +.##.. +..... +---- 0xc9 ('.') */ +..... +.#.#. +..... +.##.. +#.##. +##... +.##.. +..... +---- 0xca ('.') */ +.#... +..#.. +..... +.##.. +..#.. +..#.. +.###. +..... +---- 0xcb ('.') */ +...#. +..#.. +..... +.##.. +..#.. +..#.. +.###. +..... +---- 0xcc ('.') */ +..#.. +.#.#. +..... +.##.. +..#.. +..#.. +.###. +..... +---- 0xcd ('.') */ +..... +.#.#. +..... +.##.. +..#.. +..#.. +.###. +..... +---- 0xce ('.') */ +#.#.. +.#... +#.#.. +...#. +.###. +#..#. +.##.. +..... +---- 0xcf ('.') */ +.#.#. +#.#.. +..... +###.. +#..#. +#..#. +#..#. +..... +---- 0xd0 ('.') */ +.#... +..#.. +..... +.##.. +#..#. +#..#. +.##.. +..... +---- 0xd1 ('.') */ +..#.. +.#... +..... +.##.. +#..#. +#..#. +.##.. +..... +---- 0xd2 ('.') */ +.##.. +#..#. +..... +.##.. +#..#. +#..#. +.##.. +..... +---- 0xd3 ('.') */ +.#.#. +#.#.. +..... +.##.. +#..#. +#..#. +.##.. +..... +---- 0xd4 ('.') */ +..... +#..#. +..... +.##.. +#..#. +#..#. +.##.. +..... +---- 0xd5 ('.') */ +..... +..... +..#.. +..... +.###. +..... +..#.. +..... +---- 0xd6 ('.') */ +..... +..... +..... +.###. +#.##. +##.#. +###.. +..... +---- 0xd7 ('.') */ +.#... +..#.. +..... +#..#. +#..#. +#..#. +.###. +..... +---- 0xd8 ('.') */ +..#.. +.#... +..... +#..#. +#..#. +#..#. +.###. +..... +---- 0xd9 ('.') */ +.##.. +#..#. +..... +#..#. +#..#. +#..#. +.###. +..... +---- 0xda ('.') */ +..... +#..#. +..... +#..#. +#..#. +#..#. +.###. +..... +---- 0xdb ('.') */ +..#.. +.#... +..... +#..#. +#..#. +.###. +#..#. +.##.. +---- 0xdc ('.') */ +..... +#.... +#.... +###.. +#..#. +###.. +#.... +#.... +---- 0xdd ('.') */ +..... +#..#. +..... +#..#. +#..#. +.###. +#..#. +.##.. +---- 0xde ('.') */ +####. +..... +.##.. +#..#. +####. +#..#. +#..#. +..... +---- 0xdf ('.') */ +..... +####. +..... +.###. +#..#. +#..#. +.###. +..... +---- 0xe0 ('.') */ +#..#. +.##.. +.##.. +#..#. +####. +#..#. +#..#. +..... +---- 0xe1 ('.') */ +#..#. +.##.. +..... +.###. +#..#. +#..#. +.###. +..... +---- 0xe2 ('.') */ +..... +.##.. +#..#. +#..#. +####. +#..#. +#..#. +..##. +---- 0xe3 ('.') */ +..... +..... +..... +.###. +#..#. +#..#. +.###. +..#.. +---- 0xe4 ('.') */ +..#.. +.#... +.##.. +#..#. +#.... +#..#. +.##.. +..... +---- 0xe5 ('.') */ +...#. +..#.. +..... +..##. +.#... +.#... +..##. +..... +---- 0xe6 ('.') */ +.##.. +#..#. +.##.. +#..#. +#.... +#..#. +.##.. +..... +---- 0xe7 ('.') */ +.##.. +#..#. +..... +.##.. +#.... +#.... +.##.. +..... +---- 0xe8 ('.') */ +.#... +..... +.##.. +#..#. +#.... +#..#. +.##.. +..... +---- 0xe9 ('.') */ +..... +..#.. +..... +..##. +.#... +.#... +..##. +..... +---- 0xea ('.') */ +#..#. +.##.. +.##.. +#..#. +#.... +#..#. +.##.. +..... +---- 0xeb ('.') */ +#..#. +.##.. +..... +.##.. +#.... +#.... +.##.. +..... +---- 0xec ('.') */ +#..#. +.##.. +###.. +#..#. +#..#. +#..#. +###.. +..... +---- 0xed ('.') */ +#.#.. +.#.#. +...#. +.###. +#..#. +#..#. +.###. +..... +---- 0xee ('.') */ +..... +.###. +.#..# +###.# +.#..# +.#..# +.###. +..... +---- 0xef ('.') */ +..... +..#.. +.###. +..#.. +.##.. +#.#.. +.##.. +..... +---- 0xf0 ('.') */ +####. +..... +####. +#.... +###.. +#.... +####. +..... +---- 0xf1 ('.') */ +..... +####. +..... +.##.. +#.##. +##... +.##.. +..... +---- 0xf2 ('.') */ +#..#. +.##.. +####. +#.... +###.. +#.... +####. +..... +---- 0xf3 ('.') */ +#..#. +.##.. +..... +.##.. +#.##. +##... +.##.. +..... +---- 0xf4 ('.') */ +.##.. +..... +####. +#.... +###.. +#.... +####. +..... +---- 0xf5 ('.') */ +..... +.##.. +..... +.##.. +#.##. +##... +.##.. +..... +---- 0xf6 ('.') */ +..... +####. +#.... +###.. +#.... +#.... +####. +..#.. +---- 0xf7 ('.') */ +..... +..... +..... +.##.. +#.##. +##... +.##.. +..#.. +---- 0xf8 ('.') */ +#..#. +.##.. +####. +#.... +###.. +#.... +####. +..... +---- 0xf9 ('.') */ +#..#. +.##.. +..... +.##.. +#.##. +##... +.##.. +..... +---- 0xfa ('.') */ +.##.. +#..#. +.###. +#.... +#.##. +#..#. +.##.. +..... +---- 0xfb ('.') */ +.##.. +#..#. +..... +.##.. +#..#. +.###. +...#. +.##.. +---- 0xfc ('.') */ +#..#. +.##.. +.###. +#.... +#.##. +#..#. +.##.. +..... +---- 0xfd ('.') */ +#..#. +.##.. +..... +.##.. +#..#. +.###. +...#. +.##.. +---- 0xfe ('.') */ +.##.. +..... +.###. +#.... +#.##. +#..#. +.##.. +..... +---- 0xff ('.') */ + + diff --git a/engines/sci/tools/6x10.font b/engines/sci/tools/6x10.font new file mode 100644 index 0000000000..c672d07b98 --- /dev/null +++ b/engines/sci/tools/6x10.font @@ -0,0 +1,2819 @@ +# 256 10 +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x00 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x01 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x02 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x03 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x04 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x05 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x06 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x07 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x08 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x09 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x0a ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x0b ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x0c ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x0d ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x0e ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x0f ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x10 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x11 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x12 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x13 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x14 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x15 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x16 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x17 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x18 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x19 ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x1a ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x1b ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x1c ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x1d ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x1e ('.') */ +...... +#.#.#. +...... +#...#. +...... +#...#. +...... +#.#.#. +...... +...... +---- 0x1f ('.') */ +.... +.... +.... +.... +.... +.... +.... +.... +.... +.... +---- 0x20 (' ') */ +.... +.#.. +.#.. +.#.. +.#.. +.#.. +.... +.#.. +.... +.... +---- 0x21 ('!') */ +.... +#.#. +#.#. +#.#. +.... +.... +.... +.... +.... +.... +---- 0x22 ('"') */ +...... +.#.#.. +.#.#.. +#####. +.#.#.. +#####. +.#.#.. +.#.#.. +...... +...... +---- 0x23 ('#') */ +...... +..#... +.###.. +#.#... +.###.. +..#.#. +.###.. +..#... +...... +...... +---- 0x24 ('$') */ +...... +.#..#. +#.#.#. +.#.#.. +..#... +.#.#.. +#.#.#. +#..#.. +...... +...... +---- 0x25 ('%') */ +...... +.#.... +#.#... +#.#... +.#.... +#.#.#. +#..#.. +.##.#. +...... +...... +---- 0x26 ('&') */ +.. +#. +#. +#. +.. +.. +.. +.. +.. +.. +---- 0x27 (''') */ +..... +...#. +..#.. +.#... +.#... +.#... +..#.. +...#. +..... +..... +---- 0x28 ('(') */ +..... +.#... +..#.. +...#. +...#. +...#. +..#.. +.#... +..... +..... +---- 0x29 (')') */ +...... +...... +#...#. +.#.#.. +#####. +.#.#.. +#...#. +...... +...... +...... +---- 0x2a ('*') */ +...... +...... +..#... +..#... +#####. +..#... +..#... +...... +...... +...... +---- 0x2b ('+') */ +.... +.... +.... +.... +.... +.... +.##. +.#.. +#... +.... +---- 0x2c (',') */ +...... +...... +...... +...... +#####. +...... +...... +...... +...... +...... +---- 0x2d ('-') */ +..... +..... +..... +..... +..... +..... +.##.. +.##.. +..... +..... +---- 0x2e ('.') */ +....... +......# +.....#. +....#.. +...#... +..#.... +.#..... +#...... +....... +....... +---- 0x2f ('/') */ +...... +..#... +.#.#.. +#...#. +#...#. +#...#. +.#.#.. +..#... +...... +...... +---- 0x30 ('0') */ +.... +.#.. +##.. +.#.. +.#.. +.#.. +.#.. +###. +.... +.... +---- 0x31 ('1') */ +...... +.###.. +#...#. +....#. +..##.. +.#.... +#..... +#####. +...... +...... +---- 0x32 ('2') */ +...... +#####. +....#. +...#.. +..##.. +....#. +#...#. +.###.. +...... +...... +---- 0x33 ('3') */ +...... +...#.. +..##.. +.#.#.. +#..#.. +#####. +...#.. +...#.. +...... +...... +---- 0x34 ('4') */ +...... +#####. +#..... +#.##.. +##..#. +....#. +#...#. +.###.. +...... +...... +---- 0x35 ('5') */ +...... +..##.. +.#.... +#..... +#.##.. +##..#. +#...#. +.###.. +...... +...... +---- 0x36 ('6') */ +...... +#####. +....#. +...#.. +...#.. +..#... +.#.... +.#.... +...... +...... +---- 0x37 ('7') */ +...... +.###.. +#...#. +#...#. +.###.. +#...#. +#...#. +.###.. +...... +...... +---- 0x38 ('8') */ +...... +.###.. +#...#. +#..##. +.##.#. +....#. +...#.. +.##... +...... +...... +---- 0x39 ('9') */ +..... +..... +.##.. +.##.. +..... +..... +.##.. +.##.. +..... +..... +---- 0x3a (':') */ +..... +..... +.##.. +.##.. +..... +..... +.##.. +.#... +#.... +..... +---- 0x3b (';') */ +...... +....#. +...#.. +..#... +.#.... +..#... +...#.. +....#. +...... +...... +---- 0x3c ('<') */ +...... +...... +...... +#####. +...... +#####. +...... +...... +...... +...... +---- 0x3d ('=') */ +...... +.#.... +..#... +...#.. +....#. +...#.. +..#... +.#.... +...... +...... +---- 0x3e ('>') */ +...... +.###.. +#...#. +...#.. +..#... +..#... +...... +..#... +...... +...... +---- 0x3f ('?') */ +...... +.###.. +#...#. +#..##. +#.#.#. +#.##.. +#..... +.###.. +...... +...... +---- 0x40 ('@') */ +...... +..#... +.#.#.. +#...#. +#...#. +#####. +#...#. +#...#. +...... +...... +---- 0x41 ('A') */ +...... +####.. +.#..#. +.#..#. +.###.. +.#..#. +.#..#. +####.. +...... +...... +---- 0x42 ('B') */ +...... +.###.. +#...#. +#..... +#..... +#..... +#...#. +.###.. +...... +...... +---- 0x43 ('C') */ +...... +####.. +.#..#. +.#..#. +.#..#. +.#..#. +.#..#. +####.. +...... +...... +---- 0x44 ('D') */ +...... +#####. +#..... +#..... +####.. +#..... +#..... +#####. +...... +...... +---- 0x45 ('E') */ +...... +#####. +#..... +#..... +####.. +#..... +#..... +#..... +...... +...... +---- 0x46 ('F') */ +...... +.###.. +#...#. +#..... +#..... +#..##. +#...#. +.###.. +...... +...... +---- 0x47 ('G') */ +...... +#...#. +#...#. +#...#. +#####. +#...#. +#...#. +#...#. +...... +...... +---- 0x48 ('H') */ +.... +###. +.#.. +.#.. +.#.. +.#.. +.#.. +###. +.... +.... +---- 0x49 ('I') */ +...... +..###. +...#.. +...#.. +...#.. +...#.. +#..#.. +.##... +...... +...... +---- 0x4a ('J') */ +...... +#...#. +#..#.. +#.#... +##.... +#.#... +#..#.. +#...#. +...... +...... +---- 0x4b ('K') */ +...... +#..... +#..... +#..... +#..... +#..... +#..... +#####. +...... +...... +---- 0x4c ('L') */ +...... +#...#. +#...#. +##.##. +#.#.#. +#...#. +#...#. +#...#. +...... +...... +---- 0x4d ('M') */ +...... +#...#. +#...#. +##..#. +#.#.#. +#..##. +#...#. +#...#. +...... +...... +---- 0x4e ('N') */ +...... +.###.. +#...#. +#...#. +#...#. +#...#. +#...#. +.###.. +...... +...... +---- 0x4f ('O') */ +...... +####.. +#...#. +#...#. +####.. +#..... +#..... +#..... +...... +...... +---- 0x50 ('P') */ +...... +.###.. +#...#. +#...#. +#...#. +#...#. +#.#.#. +.###.. +....#. +...... +---- 0x51 ('Q') */ +...... +####.. +#...#. +#...#. +####.. +#.#... +#..#.. +#...#. +...... +...... +---- 0x52 ('R') */ +...... +.###.. +#...#. +#..... +.###.. +....#. +#...#. +.###.. +...... +...... +---- 0x53 ('S') */ +...... +#####. +..#... +..#... +..#... +..#... +..#... +..#... +...... +...... +---- 0x54 ('T') */ +...... +#...#. +#...#. +#...#. +#...#. +#...#. +#...#. +.###.. +...... +...... +---- 0x55 ('U') */ +...... +#...#. +#...#. +#...#. +.#.#.. +.#.#.. +.#.#.. +..#... +...... +...... +---- 0x56 ('V') */ +...... +#...#. +#...#. +#...#. +#.#.#. +#.#.#. +##.##. +#...#. +...... +...... +---- 0x57 ('W') */ +...... +#...#. +#...#. +.#.#.. +..#... +.#.#.. +#...#. +#...#. +...... +...... +---- 0x58 ('X') */ +...... +#...#. +#...#. +.#.#.. +..#... +..#... +..#... +..#... +...... +...... +---- 0x59 ('Y') */ +...... +#####. +....#. +...#.. +..#... +.#.... +#..... +#####. +...... +...... +---- 0x5a ('Z') */ +..... +.###. +.#... +.#... +.#... +.#... +.#... +.###. +..... +..... +---- 0x5b ('[') */ +....... +#...... +.#..... +..#.... +...#... +....#.. +.....#. +......# +....... +....... +---- 0x5c ('\') */ +..... +.###. +...#. +...#. +...#. +...#. +...#. +.###. +..... +..... +---- 0x5d (']') */ +...... +..#... +.#.#.. +#...#. +...... +...... +...... +...... +...... +...... +---- 0x5e ('^') */ +..... +..... +..... +..... +..... +..... +..... +..... +##### +..... +---- 0x5f ('_') */ +#.. +.#. +... +... +... +... +... +... +... +... +---- 0x60 ('`') */ +...... +...... +...... +.###.. +....#. +.####. +#...#. +.####. +...... +...... +---- 0x61 ('a') */ +...... +#..... +#..... +#.##.. +##..#. +#...#. +##..#. +#.##.. +...... +...... +---- 0x62 ('b') */ +...... +...... +...... +.###.. +#...#. +#..... +#...#. +.###.. +...... +...... +---- 0x63 ('c') */ +...... +....#. +....#. +.##.#. +#..##. +#...#. +#..##. +.##.#. +...... +...... +---- 0x64 ('d') */ +...... +...... +...... +.###.. +#...#. +#####. +#..... +.###.. +...... +...... +---- 0x65 ('e') */ +...... +..##.. +.#..#. +.#.... +####.. +.#.... +.#.... +.#.... +...... +...... +---- 0x66 ('f') */ +...... +...... +...... +.####. +#...#. +#...#. +.####. +....#. +#...#. +.###.. +---- 0x67 ('g') */ +...... +#..... +#..... +#.##.. +##..#. +#...#. +#...#. +#...#. +...... +...... +---- 0x68 ('h') */ +.... +.#.. +.... +##.. +.#.. +.#.. +.#.. +###. +.... +.... +---- 0x69 ('i') */ +..... +...#. +..... +..##. +...#. +...#. +...#. +#..#. +#..#. +.##.. +---- 0x6a ('j') */ +...... +#..... +#..... +#...#. +#..#.. +###... +#..#.. +#...#. +...... +...... +---- 0x6b ('k') */ +.... +##.. +.#.. +.#.. +.#.. +.#.. +.#.. +###. +.... +.... +---- 0x6c ('l') */ +...... +...... +...... +##.#.. +#.#.#. +#.#.#. +#.#.#. +#...#. +...... +...... +---- 0x6d ('m') */ +...... +...... +...... +#.##.. +##..#. +#...#. +#...#. +#...#. +...... +...... +---- 0x6e ('n') */ +...... +...... +...... +.###.. +#...#. +#...#. +#...#. +.###.. +...... +...... +---- 0x6f ('o') */ +...... +...... +...... +#.##.. +##..#. +#...#. +##..#. +#.##.. +#..... +#..... +---- 0x70 ('p') */ +...... +...... +...... +.##.#. +#..##. +#...#. +#..##. +.##.#. +....#. +....#. +---- 0x71 ('q') */ +...... +...... +...... +#.##.. +##..#. +#..... +#..... +#..... +...... +...... +---- 0x72 ('r') */ +...... +...... +...... +.###.. +#..... +.###.. +....#. +####.. +...... +...... +---- 0x73 ('s') */ +...... +.#.... +.#.... +####.. +.#.... +.#.... +.#..#. +..##.. +...... +...... +---- 0x74 ('t') */ +...... +...... +...... +#...#. +#...#. +#...#. +#..##. +.##.#. +...... +...... +---- 0x75 ('u') */ +...... +...... +...... +#...#. +#...#. +.#.#.. +.#.#.. +..#... +...... +...... +---- 0x76 ('v') */ +...... +...... +...... +#...#. +#...#. +#.#.#. +#.#.#. +.#.#.. +...... +...... +---- 0x77 ('w') */ +...... +...... +...... +#...#. +.#.#.. +..#... +.#.#.. +#...#. +...... +...... +---- 0x78 ('x') */ +...... +...... +...... +#...#. +#...#. +#..##. +.##.#. +....#. +#...#. +.###.. +---- 0x79 ('y') */ +...... +...... +...... +#####. +...#.. +..#... +.#.... +#####. +...... +...... +---- 0x7a ('z') */ +...... +...##. +..#... +...#.. +.##... +...#.. +..#... +...##. +...... +...... +---- 0x7b ('{') */ +... +.#. +.#. +.#. +.#. +.#. +.#. +.#. +... +... +---- 0x7c ('|') */ +...... +.##... +...#.. +..#... +...##. +..#... +...#.. +.##... +...... +...... +---- 0x7d ('}') */ +...... +.#..#. +#.#.#. +#..#.. +...... +...... +...... +...... +...... +...... +---- 0x7e ('~') */ +...... +...... +...... +...... +...... +...... +...... +...... +...... +...... +---- 0x7f ('.') */ +...... +..#... +...... +..#... +..#... +..#... +..#... +..#... +...... +...... +---- 0x80 ('.') */ +...... +...... +..#... +.####. +#.#... +#.#... +#.#... +.####. +..#... +...... +---- 0x81 ('.') */ +...... +..##.. +.#..#. +.#.... +###... +.#.... +.#..#. +#.##.. +...... +...... +---- 0x82 ('.') */ +...... +...... +...... +#...#. +.###.. +.#.#.. +.###.. +#...#. +...... +...... +---- 0x83 ('.') */ +...... +#...#. +#...#. +.#.#.. +..#... +#####. +..#... +..#... +..#... +...... +---- 0x84 ('.') */ +...... +..#... +..#... +..#... +...... +..#... +..#... +..#... +...... +...... +---- 0x85 ('.') */ +...... +.###.. +#..... +###... +#..#.. +.#..#. +..###. +....#. +.###.. +...... +---- 0x86 ('.') */ +.#.#.. +...... +...... +...... +...... +...... +...... +...... +...... +...... +---- 0x87 ('.') */ +...... +.###.. +#...#. +#.#.#. +##..#. +#.#.#. +#...#. +.###.. +...... +...... +---- 0x88 ('.') */ +...... +..###. +.#..#. +.#.##. +..#.#. +...... +.####. +...... +...... +...... +---- 0x89 ('.') */ +...... +...... +...... +..#..# +.#..#. +#..#.. +.#..#. +..#..# +...... +...... +---- 0x8a ('.') */ +...... +...... +...... +...... +.####. +....#. +...... +...... +...... +...... +---- 0x8b ('.') */ +...... +...... +...... +...... +.####. +...... +...... +...... +...... +...... +---- 0x8c ('.') */ +...... +.###.. +#...#. +###.#. +##..#. +##..#. +#...#. +.###.. +...... +...... +---- 0x8d ('.') */ +#####. +...... +...... +...... +...... +...... +...... +...... +...... +...... +---- 0x8e ('.') */ +...... +..#... +.#.#.. +..#... +...... +...... +...... +...... +...... +...... +---- 0x8f ('.') */ +...... +...... +..#... +..#... +#####. +..#... +..#... +#####. +...... +...... +---- 0x90 ('.') */ +..##.. +.#..#. +...#.. +..#... +.####. +...... +...... +...... +...... +...... +---- 0x91 ('.') */ +.###.. +....#. +..##.. +....#. +.###.. +...... +...... +...... +...... +...... +---- 0x92 ('.') */ +...#.. +..#... +...... +...... +...... +...... +...... +...... +...... +...... +---- 0x93 ('.') */ +...... +...... +...... +#...#. +#...#. +#...#. +##..#. +#.##.. +#..... +...... +---- 0x94 ('.') */ +...... +.####. +###.#. +###.#. +.##.#. +..#.#. +..#.#. +..#.#. +...... +...... +---- 0x95 ('.') */ +...... +...... +...... +...... +..#... +...... +...... +...... +...... +...... +---- 0x96 ('.') */ +...... +...... +...... +...... +...... +...... +...... +...... +...#.. +..#... +---- 0x97 ('.') */ +..#... +.##... +..#... +..#... +.###.. +...... +...... +...... +...... +...... +---- 0x98 ('.') */ +...... +..##.. +.#..#. +.#..#. +..##.. +...... +.####. +...... +...... +...... +---- 0x99 ('.') */ +...... +...... +...... +#..#.. +.#..#. +..#..# +.#..#. +#..#.. +...... +...... +---- 0x9a ('.') */ +.#.... +##.... +.#.... +.#.... +###..# +....## +...#.# +..#### +.....# +...... +---- 0x9b ('.') */ +.#.... +##.... +.#.... +.#.... +###.#. +...#.# +.....# +....#. +...### +...... +---- 0x9c ('.') */ +##.... +..#... +.#.... +..#... +##..#. +...##. +..#.#. +.####. +....#. +...... +---- 0x9d ('.') */ +...... +..#... +...... +..#... +..#... +.#.... +#...#. +.###.. +...... +...... +---- 0x9e ('.') */ +.#.... +..#... +.###.. +#...#. +#...#. +#####. +#...#. +#...#. +...... +...... +---- 0x9f ('.') */ +...#.. +..#... +.###.. +#...#. +#...#. +#####. +#...#. +#...#. +...... +...... +---- 0xa0 ('.') */ +..#... +.#.#.. +.###.. +#...#. +#...#. +#####. +#...#. +#...#. +...... +...... +---- 0xa1 ('.') */ +.#..#. +#.##.. +.###.. +#...#. +#...#. +#####. +#...#. +#...#. +...... +...... +---- 0xa2 ('.') */ +.#.#.. +...... +.###.. +#...#. +#...#. +#####. +#...#. +#...#. +...... +...... +---- 0xa3 ('.') */ +..#... +.#.#.. +.###.. +#...#. +#...#. +#####. +#...#. +#...#. +...... +...... +---- 0xa4 ('.') */ +...... +..#### +.#.#.. +#..#.. +#..### +####.. +#..#.. +#..### +...... +...... +---- 0xa5 ('.') */ +...... +.###.. +#...#. +#..... +#..... +#..... +#...#. +.###.. +..#... +.#.... +---- 0xa6 ('.') */ +.#.... +#####. +#..... +#..... +####.. +#..... +#..... +#####. +...... +...... +---- 0xa7 ('.') */ +...#.. +#####. +#..... +#..... +####.. +#..... +#..... +#####. +...... +...... +---- 0xa8 ('.') */ +..#... +#####. +#..... +#..... +####.. +#..... +#..... +#####. +...... +...... +---- 0xa9 ('.') */ +.#.#.. +#####. +#..... +#..... +####.. +#..... +#..... +#####. +...... +...... +---- 0xaa ('.') */ +.#.... +..#... +.###.. +..#... +..#... +..#... +..#... +.###.. +...... +...... +---- 0xab ('.') */ +...#.. +..#... +.###.. +..#... +..#... +..#... +..#... +.###.. +...... +...... +---- 0xac ('.') */ +..#... +.#.#.. +.###.. +..#... +..#... +..#... +..#... +.###.. +...... +...... +---- 0xad ('.') */ +.#.#.. +...... +.###.. +..#... +..#... +..#... +..#... +.###.. +...... +...... +---- 0xae ('.') */ +...... +####.. +.#..#. +.#..#. +###.#. +.#..#. +.#..#. +####.. +...... +...... +---- 0xaf ('.') */ +..#.#. +.#.#.. +#...#. +##..#. +#.#.#. +#..##. +#...#. +#...#. +...... +...... +---- 0xb0 ('.') */ +.#.... +..#... +.###.. +#...#. +#...#. +#...#. +#...#. +.###.. +...... +...... +---- 0xb1 ('.') */ +...#.. +..#... +.###.. +#...#. +#...#. +#...#. +#...#. +.###.. +...... +...... +---- 0xb2 ('.') */ +..#... +.#.#.. +.###.. +#...#. +#...#. +#...#. +#...#. +.###.. +...... +...... +---- 0xb3 ('.') */ +..#.#. +.#.#.. +.###.. +#...#. +#...#. +#...#. +#...#. +.###.. +...... +...... +---- 0xb4 ('.') */ +.#.#.. +...... +.###.. +#...#. +#...#. +#...#. +#...#. +.###.. +...... +...... +---- 0xb5 ('.') */ +...... +...... +...... +#...#. +.#.#.. +..#... +.#.#.. +#...#. +...... +...... +---- 0xb6 ('.') */ +...... +.###.. +#..##. +#..##. +#.#.#. +##..#. +##..#. +.###.. +...... +...... +---- 0xb7 ('.') */ +.#.... +..#... +#...#. +#...#. +#...#. +#...#. +#...#. +.###.. +...... +...... +---- 0xb8 ('.') */ +...#.. +..#... +#...#. +#...#. +#...#. +#...#. +#...#. +.###.. +...... +...... +---- 0xb9 ('.') */ +..#... +.#.#.. +...... +#...#. +#...#. +#...#. +#...#. +.###.. +...... +...... +---- 0xba ('.') */ +.#.#.. +...... +#...#. +#...#. +#...#. +#...#. +#...#. +.###.. +...... +...... +---- 0xbb ('.') */ +...#.. +..#... +#...#. +#...#. +.#.#.. +..#... +..#... +..#... +...... +...... +---- 0xbc ('.') */ +...... +#..... +####.. +#...#. +####.. +#..... +#..... +#..... +...... +...... +---- 0xbd ('.') */ +...... +.###.. +#...#. +#..#.. +#.#... +#..#.. +#...#. +#.##.. +...... +...... +---- 0xbe ('.') */ +.#.... +..#... +...... +.###.. +....#. +.####. +#...#. +.####. +...... +...... +---- 0xbf ('.') */ +...#.. +..#... +...... +.###.. +....#. +.####. +#...#. +.####. +...... +...... +---- 0xc0 ('.') */ +..#... +.#.#.. +...... +.###.. +....#. +.####. +#...#. +.####. +...... +...... +---- 0xc1 ('.') */ +..#.#. +.#.#.. +...... +.###.. +....#. +.####. +#...#. +.####. +...... +...... +---- 0xc2 ('.') */ +...... +.#.#.. +...... +.###.. +....#. +.####. +#...#. +.####. +...... +...... +---- 0xc3 ('.') */ +..#... +.#.#.. +..#... +.###.. +....#. +.####. +#...#. +.####. +...... +...... +---- 0xc4 ('.') */ +...... +...... +...... +.####. +...#.# +.##### +#..#.. +.##### +...... +...... +---- 0xc5 ('.') */ +...... +...... +...... +.###.. +#...#. +#..... +#...#. +.###.. +..#... +.#.... +---- 0xc6 ('.') */ +.#.... +..#... +...... +.###.. +#...#. +#####. +#..... +.###.. +...... +...... +---- 0xc7 ('.') */ +...#.. +..#... +...... +.###.. +#...#. +#####. +#..... +.###.. +...... +...... +---- 0xc8 ('.') */ +..#... +.#.#.. +...... +.###.. +#...#. +#####. +#..... +.###.. +...... +...... +---- 0xc9 ('.') */ +...... +.#.#.. +...... +.###.. +#...#. +#####. +#..... +.###.. +...... +...... +---- 0xca ('.') */ +.#.... +..#... +...... +.##... +..#... +..#... +..#... +.###.. +...... +...... +---- 0xcb ('.') */ +..#... +.#.... +...... +.##... +..#... +..#... +..#... +.###.. +...... +...... +---- 0xcc ('.') */ +..#... +.#.#.. +...... +.##... +..#... +..#... +..#... +.###.. +...... +...... +---- 0xcd ('.') */ +...... +.#.#.. +...... +.##... +..#... +..#... +..#... +.###.. +...... +...... +---- 0xce ('.') */ +...... +##.... +..##.. +.###.. +#...#. +#...#. +#...#. +.###.. +...... +...... +---- 0xcf ('.') */ +..#.#. +.#.#.. +...... +#.##.. +##..#. +#...#. +#...#. +#...#. +...... +...... +---- 0xd0 ('.') */ +.#.... +..#... +...... +.###.. +#...#. +#...#. +#...#. +.###.. +...... +...... +---- 0xd1 ('.') */ +...#.. +..#... +...... +.###.. +#...#. +#...#. +#...#. +.###.. +...... +...... +---- 0xd2 ('.') */ +..#... +.#.#.. +...... +.###.. +#...#. +#...#. +#...#. +.###.. +...... +...... +---- 0xd3 ('.') */ +..#.#. +.#.#.. +...... +.###.. +#...#. +#...#. +#...#. +.###.. +...... +...... +---- 0xd4 ('.') */ +...... +.#.#.. +...... +.###.. +#...#. +#...#. +#...#. +.###.. +...... +...... +---- 0xd5 ('.') */ +...... +...... +..#... +...... +#####. +...... +..#... +...... +...... +...... +---- 0xd6 ('.') */ +...... +...... +...... +.####. +#..##. +#.#.#. +##..#. +####.. +...... +...... +---- 0xd7 ('.') */ +.#.... +..#... +...... +#...#. +#...#. +#...#. +#..##. +.##.#. +...... +...... +---- 0xd8 ('.') */ +...#.. +..#... +...... +#...#. +#...#. +#...#. +#..##. +.##.#. +...... +...... +---- 0xd9 ('.') */ +..#... +.#.#.. +...... +#...#. +#...#. +#...#. +#..##. +.##.#. +...... +...... +---- 0xda ('.') */ +...... +.#.#.. +...... +#...#. +#...#. +#...#. +#..##. +.##.#. +...... +...... +---- 0xdb ('.') */ +...... +...#.. +..#... +#...#. +#...#. +#..##. +.##.#. +....#. +#...#. +.###.. +---- 0xdc ('.') */ +...... +...... +#..... +####.. +#...#. +#...#. +#...#. +####.. +#..... +#..... +---- 0xdd ('.') */ +...... +.#.#.. +...... +#...#. +#...#. +#..##. +.##.#. +....#. +#...#. +.###.. +---- 0xde ('.') */ +.###.. +...... +.###.. +#...#. +#...#. +#####. +#...#. +#...#. +...... +...... +---- 0xdf ('.') */ +...... +.###.. +...... +.###.. +....#. +.####. +#...#. +.####. +...... +...... +---- 0xe0 ('.') */ +#...#. +.###.. +.###.. +#...#. +#...#. +#####. +#...#. +#...#. +...... +...... +---- 0xe1 ('.') */ +#...#. +.###.. +...... +.###.. +....#. +.####. +#...#. +.####. +...... +...... +---- 0xe2 ('.') */ +...... +..#... +.#.#.. +#...#. +#...#. +#####. +#...#. +#...#. +...#.. +...##. +---- 0xe3 ('.') */ +...... +...... +...... +.###.. +....#. +.####. +#...#. +.####. +...#.. +...##. +---- 0xe4 ('.') */ +...#.. +..#... +.###.. +#...#. +#..... +#..... +#...#. +.###.. +...... +...... +---- 0xe5 ('.') */ +...#.. +..#... +...... +.###.. +#...#. +#..... +#...#. +.###.. +...... +...... +---- 0xe6 ('.') */ +..#... +.#.#.. +.###.. +#...#. +#..... +#..... +#...#. +.###.. +...... +...... +---- 0xe7 ('.') */ +..#... +.#.#.. +...... +.###.. +#...#. +#..... +#...#. +.###.. +...... +...... +---- 0xe8 ('.') */ +..#... +...... +.###.. +#...#. +#..... +#..... +#...#. +.###.. +...... +...... +---- 0xe9 ('.') */ +...... +..#... +...... +.###.. +#...#. +#..... +#...#. +.###.. +...... +...... +---- 0xea ('.') */ +.#.#.. +..#... +.###.. +#...#. +#..... +#..... +#...#. +.###.. +...... +...... +---- 0xeb ('.') */ +.#.#.. +..#... +...... +.###.. +#...#. +#..... +#...#. +.###.. +...... +...... +---- 0xec ('.') */ +.#.#.. +..#... +####.. +#...#. +#...#. +#...#. +#...#. +####.. +...... +...... +---- 0xed ('.') */ +.#.#.. +..#... +....#. +.##.#. +#..##. +#...#. +#..##. +.##.#. +...... +...... +---- 0xee ('.') */ +...... +.####. +.#...# +.#...# +###..# +.#...# +.#...# +.####. +...... +...... +---- 0xef ('.') */ +...... +...#.. +..###. +...#.. +.###.. +#..#.. +#..#.. +.###.. +...... +...... +---- 0xf0 ('.') */ +.###.. +...... +#####. +#..... +####.. +#..... +#..... +#####. +...... +...... +---- 0xf1 ('.') */ +...... +.###.. +...... +.###.. +#...#. +#####. +#..... +.###.. +...... +...... +---- 0xf2 ('.') */ +#...#. +.###.. +#####. +#..... +####.. +#..... +#..... +#####. +...... +...... +---- 0xf3 ('.') */ +#...#. +.###.. +...... +.###.. +#...#. +#####. +#..... +.###.. +...... +...... +---- 0xf4 ('.') */ +..#... +...... +#####. +#..... +####.. +#..... +#..... +#####. +...... +...... +---- 0xf5 ('.') */ +...... +..#... +...... +.###.. +#...#. +#####. +#..... +.###.. +...... +...... +---- 0xf6 ('.') */ +...... +#####. +#..... +#..... +####.. +#..... +#..... +#####. +..#... +..##.. +---- 0xf7 ('.') */ +...... +...... +...... +.###.. +#...#. +#####. +#..... +.###.. +..#... +..##.. +---- 0xf8 ('.') */ +.#.#.. +..#... +#####. +#..... +####.. +#..... +#..... +#####. +...... +...... +---- 0xf9 ('.') */ +.#.#.. +..#... +...... +.###.. +#...#. +#####. +#..... +.###.. +...... +...... +---- 0xfa ('.') */ +..#... +.#.#.. +.###.. +#...#. +#..... +#..##. +#...#. +.###.. +...... +...... +---- 0xfb ('.') */ +..#... +.#.#.. +...... +.##.#. +#..#.. +.##... +#..... +.###.. +#...#. +.###.. +---- 0xfc ('.') */ +#...#. +.###.. +.###.. +#...#. +#..... +#..##. +#...#. +.###.. +...... +...... +---- 0xfd ('.') */ +#...#. +.###.. +...... +.##.#. +#..#.. +.##... +#..... +.###.. +#...#. +.###.. +---- 0xfe ('.') */ +..#... +...... +.###.. +#...#. +#..... +#..##. +#...#. +.###.. +...... +...... +---- 0xff ('.') */ + + diff --git a/engines/sci/tools/Makefile.am b/engines/sci/tools/Makefile.am new file mode 100644 index 0000000000..715a438251 --- /dev/null +++ b/engines/sci/tools/Makefile.am @@ -0,0 +1,19 @@ +INCLUDES = -I$(top_srcdir)/src/include @ac_png_includes@ @EXTRA_INCLUDES@ +LDADD = ../scicore/libscicore.a +EXTRA_DIST = sciunpack.h bdf.h bdfP.h +bin_PROGRAMS = sciunpack scidisasm scipack bdftofont fonttoc scimusicplayer +sciunpack_SOURCES = sciunpack.c listwords.c scriptdump.c vocabdump.c +sciunpack_LDADD = ../scicore/libscicore.a @ac_glx_libraries@ @ac_glx_libraries@ @ac_png_libraries@ +scidisasm_SOURCES = scidisasm.c +scipack_LDADD = ../scicore/libscicore.a +scipack_SOURCES = scipack.c +scimusicplayer_LDADD = ../sfx/libscisound.a ../scicore/libscicore.a \ + ../sfx/player/libsciplayer.a \ + ../sfx/seq/libsciseq.a ../sfx/timer/libscitimer.a \ + ../sfx/pcm_device/libscipcm.a ../sfx/mixer/libscimixer.a \ + ../sfx/softseq/libscisoftseq.a \ + ../sfx/libscisoundlib.a \ + ../sfx/device/libscisounddevice.a @LIB_M@ +scimusicplayer_SOURCES = musicplayer.c +bdftofont_SOURCES = bdf.c bdfgrid.c bdfgname.c bdftofont.c +fonttoc_SOURCES = fonttoc.c diff --git a/engines/sci/tools/bdf.c b/engines/sci/tools/bdf.c new file mode 100644 index 0000000000..5d97a51692 --- /dev/null +++ b/engines/sci/tools/bdf.c @@ -0,0 +1,7252 @@ +/* + * Copyright 2001 Computing Research Labs, New Mexico State University + * + * 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. + */ +#ifndef lint +#ifdef __GNUC__ +static char rcsid[] __attribute__ ((unused)) = "$Id: bdf.c 1284 2004-04-02 07:42:44Z jameson $"; +#else +static char rcsid[] = "$Id: bdf.c 1284 2004-04-02 07:42:44Z jameson $"; +#endif +#endif + +#include "bdfP.h" + +#ifdef HAVE_HBF +#include "hbf.h" +#endif + +#undef MAX +#define MAX(h, i) ((h) > (i) ? (h) : (i)) + +#undef MIN +#define MIN(l, o) ((l) < (o) ? (l) : (o)) + +/************************************************************************** + * + * Masks used for checking different bits per pixel cases. + * + **************************************************************************/ + +unsigned char onebpp[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; +unsigned char twobpp[] = { 0xc0, 0x30, 0x0c, 0x03 }; +unsigned char fourbpp[] = { 0xf0, 0x0f }; +unsigned char eightbpp[] = { 0xff }; + +/************************************************************************** + * + * Default BDF font options. + * + **************************************************************************/ + +static bdf_options_t _bdf_opts = { + 1, /* Hint TTF glyphs. */ + 1, /* Correct metrics. */ + 1, /* Preserve unencoded glyphs. */ + 1, /* Preserve comments. */ + 1, /* Pad character-cells. */ + BDF_PROPORTIONAL, /* Default spacing. */ + 12, /* Default point size. */ + 0, /* Default horizontal resolution. */ + 0, /* Default vertical resolution. */ + 1, /* Bits per pixel. */ + BDF_UNIX_EOL, /* Line separator. */ +}; + +/************************************************************************** + * + * Builtin BDF font properties. + * + **************************************************************************/ + +/* + * List of most properties that might appear in a font. Doesn't include the + * RAW_* and AXIS_* properties in X11R6 polymorphic fonts. + */ +static bdf_property_t _bdf_properties[] = { + {"ADD_STYLE_NAME", BDF_ATOM, 1}, + {"AVERAGE_WIDTH", BDF_INTEGER, 1}, + {"AVG_CAPITAL_WIDTH", BDF_INTEGER, 1}, + {"AVG_LOWERCASE_WIDTH", BDF_INTEGER, 1}, + {"CAP_HEIGHT", BDF_INTEGER, 1}, + {"CHARSET_COLLECTIONS", BDF_ATOM, 1}, + {"CHARSET_ENCODING", BDF_ATOM, 1}, + {"CHARSET_REGISTRY", BDF_ATOM, 1}, + {"COMMENT", BDF_ATOM, 1}, + {"COPYRIGHT", BDF_ATOM, 1}, + {"DEFAULT_CHAR", BDF_CARDINAL, 1}, + {"DESTINATION", BDF_CARDINAL, 1}, + {"DEVICE_FONT_NAME", BDF_ATOM, 1}, + {"END_SPACE", BDF_INTEGER, 1}, + {"FACE_NAME", BDF_ATOM, 1}, + {"FAMILY_NAME", BDF_ATOM, 1}, + {"FIGURE_WIDTH", BDF_INTEGER, 1}, + {"FONT", BDF_ATOM, 1}, + {"FONTNAME_REGISTRY", BDF_ATOM, 1}, + {"FONT_ASCENT", BDF_INTEGER, 1}, + {"FONT_DESCENT", BDF_INTEGER, 1}, + {"FOUNDRY", BDF_ATOM, 1}, + {"FULL_NAME", BDF_ATOM, 1}, + {"ITALIC_ANGLE", BDF_INTEGER, 1}, + {"MAX_SPACE", BDF_INTEGER, 1}, + {"MIN_SPACE", BDF_INTEGER, 1}, + {"NORM_SPACE", BDF_INTEGER, 1}, + {"NOTICE", BDF_ATOM, 1}, + {"PIXEL_SIZE", BDF_INTEGER, 1}, + {"POINT_SIZE", BDF_INTEGER, 1}, + {"QUAD_WIDTH", BDF_INTEGER, 1}, + {"RAW_ASCENT", BDF_INTEGER, 1}, + {"RAW_AVERAGE_WIDTH", BDF_INTEGER, 1}, + {"RAW_AVG_CAPITAL_WIDTH", BDF_INTEGER, 1}, + {"RAW_AVG_LOWERCASE_WIDTH", BDF_INTEGER, 1}, + {"RAW_CAP_HEIGHT", BDF_INTEGER, 1}, + {"RAW_DESCENT", BDF_INTEGER, 1}, + {"RAW_END_SPACE", BDF_INTEGER, 1}, + {"RAW_FIGURE_WIDTH", BDF_INTEGER, 1}, + {"RAW_MAX_SPACE", BDF_INTEGER, 1}, + {"RAW_MIN_SPACE", BDF_INTEGER, 1}, + {"RAW_NORM_SPACE", BDF_INTEGER, 1}, + {"RAW_PIXEL_SIZE", BDF_INTEGER, 1}, + {"RAW_POINT_SIZE", BDF_INTEGER, 1}, + {"RAW_PIXELSIZE", BDF_INTEGER, 1}, + {"RAW_POINTSIZE", BDF_INTEGER, 1}, + {"RAW_QUAD_WIDTH", BDF_INTEGER, 1}, + {"RAW_SMALL_CAP_SIZE", BDF_INTEGER, 1}, + {"RAW_STRIKEOUT_ASCENT", BDF_INTEGER, 1}, + {"RAW_STRIKEOUT_DESCENT", BDF_INTEGER, 1}, + {"RAW_SUBSCRIPT_SIZE", BDF_INTEGER, 1}, + {"RAW_SUBSCRIPT_X", BDF_INTEGER, 1}, + {"RAW_SUBSCRIPT_Y", BDF_INTEGER, 1}, + {"RAW_SUPERSCRIPT_SIZE", BDF_INTEGER, 1}, + {"RAW_SUPERSCRIPT_X", BDF_INTEGER, 1}, + {"RAW_SUPERSCRIPT_Y", BDF_INTEGER, 1}, + {"RAW_UNDERLINE_POSITION", BDF_INTEGER, 1}, + {"RAW_UNDERLINE_THICKNESS", BDF_INTEGER, 1}, + {"RAW_X_HEIGHT", BDF_INTEGER, 1}, + {"RELATIVE_SETWIDTH", BDF_CARDINAL, 1}, + {"RELATIVE_WEIGHT", BDF_CARDINAL, 1}, + {"RESOLUTION", BDF_INTEGER, 1}, + {"RESOLUTION_X", BDF_CARDINAL, 1}, + {"RESOLUTION_Y", BDF_CARDINAL, 1}, + {"SETWIDTH_NAME", BDF_ATOM, 1}, + {"SLANT", BDF_ATOM, 1}, + {"SMALL_CAP_SIZE", BDF_INTEGER, 1}, + {"SPACING", BDF_ATOM, 1}, + {"STRIKEOUT_ASCENT", BDF_INTEGER, 1}, + {"STRIKEOUT_DESCENT", BDF_INTEGER, 1}, + {"SUBSCRIPT_SIZE", BDF_INTEGER, 1}, + {"SUBSCRIPT_X", BDF_INTEGER, 1}, + {"SUBSCRIPT_Y", BDF_INTEGER, 1}, + {"SUPERSCRIPT_SIZE", BDF_INTEGER, 1}, + {"SUPERSCRIPT_X", BDF_INTEGER, 1}, + {"SUPERSCRIPT_Y", BDF_INTEGER, 1}, + {"UNDERLINE_POSITION", BDF_INTEGER, 1}, + {"UNDERLINE_THICKNESS", BDF_INTEGER, 1}, + {"WEIGHT", BDF_CARDINAL, 1}, + {"WEIGHT_NAME", BDF_ATOM, 1}, + {"X_HEIGHT", BDF_INTEGER, 1}, + {"_MULE_BASELINE_OFFSET", BDF_INTEGER, 1}, + {"_MULE_RELATIVE_COMPOSE", BDF_INTEGER, 1}, +}; + +static unsigned long _num_bdf_properties = +sizeof(_bdf_properties) / sizeof(_bdf_properties[0]); + +/* + * User defined properties. + */ +static bdf_property_t *user_props; +static unsigned long nuser_props = 0; + +/************************************************************************** + * + * Hash table utilities for the properties. + * + **************************************************************************/ + +#define INITIAL_HT_SIZE 241 + +typedef struct { + char *key; + void *data; +} _hashnode, *hashnode; + +typedef struct { + int limit; + int size; + int used; + hashnode *table; +} hashtable; + +typedef void (*hash_free_func)( +#ifdef __STDC__ + hashnode node +#endif +); + +static hashnode * +#ifdef __STDC__ +hash_bucket(char *key, hashtable *ht) +#else +hash_bucket(key, ht) +char *key; +hashtable *ht; +#endif +{ + char *kp = key; + unsigned long res = 0; + hashnode *bp = ht->table, *ndp; + + /* + * Mocklisp hash function. + */ + while (*kp) + res = (res << 5) - res + *kp++; + + ndp = bp + (res % ht->size); + while (*ndp) { + kp = (*ndp)->key; + if (kp[0] == key[0] && strcmp(kp, key) == 0) + break; + ndp--; + if (ndp < bp) + ndp = bp + (ht->size - 1); + } + return ndp; +} + +static void +#ifdef __STDC__ +hash_rehash(hashtable *ht) +#else +hash_rehash(ht) +hashtable *ht; +#endif +{ + hashnode *obp = ht->table, *bp, *nbp; + int i, sz = ht->size; + + ht->size <<= 1; + ht->limit = ht->size / 3; + ht->table = (hashnode *) malloc(sizeof(hashnode) * ht->size); + (void) memset((char *) ht->table, 0, sizeof(hashnode) * ht->size); + + for (i = 0, bp = obp; i < sz; i++, bp++) { + if (*bp) { + nbp = hash_bucket((*bp)->key, ht); + *nbp = *bp; + } + } + free((char *) obp); +} + +static void +#ifdef __STDC__ +hash_init(hashtable *ht) +#else +hash_init(ht) +hashtable *ht; +#endif +{ + int sz = INITIAL_HT_SIZE; + + ht->size = sz; + ht->limit = sz / 3; + ht->used = 0; + ht->table = (hashnode *) malloc(sizeof(hashnode) * sz); + (void) memset((char *) ht->table, 0, sizeof(hashnode) * sz); +} + +static void +#ifdef __STDC__ +hash_free(hashtable *ht) +#else +hash_free(ht) +hashtable *ht; +#endif +{ + int i, sz = ht->size; + hashnode *bp = ht->table; + + for (i = 0; i < sz; i++, bp++) { + if (*bp) + free((char *) *bp); + } + if (sz > 0) + free((char *) ht->table); +} + +static void +#ifdef __STDC__ +hash_insert(char *key, void *data, hashtable *ht) +#else +hash_insert(key, data, ht) +char *key; +void *data; +hashtable *ht; +#endif +{ + hashnode nn, *bp = hash_bucket(key, ht); + + nn = *bp; + if (!nn) { + *bp = nn = (hashnode) malloc(sizeof(_hashnode)); + nn->key = key; + nn->data = data; + + if (ht->used >= ht->limit) + hash_rehash(ht); + ht->used++; + } else + nn->data = data; +} + +static hashnode +#ifdef __STDC__ +hash_lookup(char *key, hashtable *ht) +#else +hash_lookup(key, ht) +char *key; +hashtable *ht; +#endif +{ + hashnode *np = hash_bucket(key, ht); + return *np; +} + +static void +#ifdef __STDC__ +hash_delete(char *name, hashtable *ht) +#else +hash_delete(name, ht) +char *name; +hashtable *ht; +#endif +{ + hashnode *hp; + + hp = hash_bucket(name, ht); + if (*hp) { + free((char *) *hp); + *hp = 0; + } +} + +/* + * The builtin property table. + */ +static hashtable proptbl; + +/************************************************************************** + * + * Utility types and functions. + * + **************************************************************************/ + +/* + * Function type for parsing lines of a BDF font. + */ +typedef int (*_bdf_line_func_t)( +#ifdef __STDC__ + char *line, + unsigned long linelen, + unsigned long lineno, + void *call_data, + void *client_data +#endif +); + +/* + * List structure for splitting lines into fields. + */ +typedef struct { + char **field; + unsigned long size; + unsigned long used; + char *bfield; + unsigned long bsize; + unsigned long bused; +} _bdf_list_t; + +/* + * Structure used while loading BDF fonts. + */ +typedef struct { + unsigned long flags; + unsigned long cnt; + unsigned long row; + unsigned long bpr; + short minlb; + short maxlb; + short maxrb; + short maxas; + short maxds; + short rbearing; + char *glyph_name; + long glyph_enc; + bdf_font_t *font; + bdf_options_t *opts; + void *client_data; + bdf_callback_t callback; + bdf_callback_struct_t cb; + unsigned long have[2048]; + _bdf_list_t list; +} _bdf_parse_t; + +#define setsbit(m, cc) (m[(cc) >> 3] |= (1 << ((cc) & 7))) +#define sbitset(m, cc) (m[(cc) >> 3] & (1 << ((cc) & 7))) + +/* + * An empty string for empty fields. + */ +static char empty[1] = { 0 }; + +/* + * Assume the line is NULL terminated and that the `list' parameter was + * initialized the first time it was used. + */ +static void +#ifdef __STDC__ +_bdf_split(char *separators, char *line, unsigned long linelen, + _bdf_list_t *list) +#else +_bdf_split(separators, line, linelen, list) +char *separators, *line; +unsigned long linelen; +_bdf_list_t *list; +#endif +{ + int mult, final_empty; + char *sp, *ep, *end; + unsigned char seps[32]; + + /* + * Initialize the list. + */ + list->used = list->bused = 0; + + /* + * If the line is empty, then simply return. + */ + if (linelen == 0 || line[0] == 0) + return; + + /* + * If the `separators' parameter is NULL or empty, split the list into + * individual bytes. + */ + if (separators == 0 || *separators == 0) { + if (linelen > list->bsize) { + if (list->bsize) + list->bfield = (char *) malloc(linelen); + else + list->bfield = (char *) realloc(list->bfield, linelen); + list->bsize = linelen; + } + list->bused = linelen; + (void) memcpy(list->bfield, line, linelen); + return; + } + + /* + * Prepare the separator bitmap. + */ + (void) memset((char *) seps, 0, 32); + + /* + * If the very last character of the separator string is a plus, then set + * the `mult' flag to indicate that multiple separators should be + * collapsed into one. + */ + for (mult = 0, sp = separators; sp && *sp; sp++) { + if (*sp == '+' && *(sp + 1) == 0) + mult = 1; + else + setsbit(seps, *sp); + } + + /* + * Break the line up into fields. + */ + for (final_empty = 0, sp = ep = line, end = sp + linelen; + sp < end && *sp;) { + /* + * Collect everything that is not a separator. + */ + for (; *ep && !sbitset(seps, *ep); ep++) ; + + /* + * Resize the list if necessary. + */ + if (list->used == list->size) { + if (list->size == 0) + list->field = (char **) malloc(sizeof(char *) * 5); + else + list->field = (char **) + realloc((char *) list->field, + sizeof(char *) * (list->size + 5)); + + list->size += 5; + } + + /* + * Assign the field appropriately. + */ + list->field[list->used++] = (ep > sp) ? sp : empty; + + sp = ep; + if (mult) { + /* + * If multiple separators should be collapsed, do it now by + * setting all the separator characters to 0. + */ + for (; *ep && sbitset(seps, *ep); ep++) + *ep = 0; + } else if (*ep != 0) + /* + * Don't collapse multiple separators by making them 0, so just + * make the one encountered 0. + */ + *ep++ = 0; + final_empty = (ep > sp && *ep == 0); + sp = ep; + } + + /* + * Finally, NULL terminate the list. + */ + if (list->used + final_empty + 1 >= list->size) { + if (list->used == list->size) { + if (list->size == 0) + list->field = (char **) malloc(sizeof(char *) * 5); + else + list->field = (char **) + realloc((char *) list->field, + sizeof(char *) * (list->size + 5)); + list->size += 5; + } + } + if (final_empty) + list->field[list->used++] = empty; + + if (list->used == list->size) { + if (list->size == 0) + list->field = (char **) malloc(sizeof(char *) * 5); + else + list->field = (char **) + realloc((char *) list->field, + sizeof(char *) * (list->size + 5)); + list->size += 5; + } + list->field[list->used] = 0; +} + +static void +#ifdef __STDC__ +_bdf_shift(unsigned long n, _bdf_list_t *list) +#else +_bdf_shift(n, list) +unsigned long n; +_bdf_list_t *list; +#endif +{ + unsigned long i, u; + + if (list == 0 || list->used == 0 || n == 0) + return; + + if (n >= list->used) { + list->used = 0; + return; + } + for (u = n, i = 0; u < list->used; i++, u++) + list->field[i] = list->field[u]; + list->used -= n; +} + +static char * +#ifdef __STDC__ +_bdf_join(int c, unsigned long *len, _bdf_list_t *list) +#else +_bdf_join(c, len, list) +int c; +unsigned long *len; +_bdf_list_t *list; +#endif +{ + unsigned long i, j; + char *fp, *dp; + + if (list == 0 || list->used == 0) + return 0; + + *len = 0; + + dp = list->field[0]; + for (i = j = 0; i < list->used; i++) { + fp = list->field[i]; + while (*fp) + dp[j++] = *fp++; + if (i + 1 < list->used) + dp[j++] = c; + } + dp[j] = 0; + + *len = j; + return dp; +} + +/* + * High speed file reader that passes each line to a callback. + */ +static int +#ifdef __STDC__ +_bdf_readlines(int fd, _bdf_line_func_t callback, void *client_data, + unsigned long *lno) +#else +_bdf_readlines(fd, callback, client_data, lno) +int fd; +_bdf_line_func_t callback; +void *client_data; +unsigned long *lno; +#endif +{ + _bdf_line_func_t cb; + unsigned long lineno; + int n, res, done, refill, bytes, hold; + char *ls, *le, *pp, *pe, *hp; + char buf[65536]; + + if (callback == 0) + return -1; + + cb = callback; + lineno = 1; + buf[0] = 0; + res = done = 0; + pp = ls = le = buf; + bytes = 65536; + while (!done && (n = read(fd, pp, bytes)) > 0) { + /* + * Determine the new end of the buffer pages. + */ + pe = pp + n; + + for (refill = 0; done == 0 && refill == 0; ) { + while (le < pe && *le != '\n' && *le != '\r') + le++; + + if (le == pe) { + /* + * Hit the end of the last page in the buffer. Need to find + * out how many pages to shift and how many pages need to be + * read in. Adjust the line start and end pointers down to + * point to the right places in the pages. + */ + pp = buf + (((ls - buf) >> 13) << 13); + n = pp - buf; + ls -= n; + le -= n; + n = pe - pp; + (void) memcpy(buf, pp, n); + pp = buf + n; + bytes = 65536 - n; + refill = 1; + } else { + /* + * Temporarily NULL terminate the line. + */ + hp = le; + hold = *le; + *le = 0; + + if (callback && *ls != '#' && *ls != 0x1a && le > ls && + (res = (*cb)(ls, le - ls, lineno, (void *) &cb, + client_data)) != 0) + done = 1; + else { + ls = ++le; + /* + * Handle the case of DOS crlf sequences. + */ + if (le < pe && hold == '\n' && *le =='\r') + ls = ++le; + } + + /* + * Increment the line number. + */ + lineno++; + + /* + * Restore the character at the end of the line. + */ + *hp = hold; + } + } + } + *lno = lineno; + return res; +} + +unsigned char * +#ifdef __STDC__ +_bdf_strdup(unsigned char *s, unsigned long len) +#else +_bdf_strdup(s, len) +unsigned char *s; +unsigned long len; +#endif +{ + unsigned char *ns; + + if (s == 0 || len == 0) + return 0; + + ns = (unsigned char *) malloc(len); + (void) memcpy((char *) ns, (char *) s, len); + return ns; +} + +void +_bdf_memmove(char *dest, char *src, unsigned long bytes) +{ + long i, j; + + i = (long) bytes; + j = i & 7; + i = (i + 7) >> 3; + + /* + * Do a memmove using Ye Olde Duff's Device for efficiency. + */ + if (src < dest) { + src += bytes; + dest += bytes; + + switch (j) { + case 0: do { + *--dest = *--src; + case 7: *--dest = *--src; + case 6: *--dest = *--src; + case 5: *--dest = *--src; + case 4: *--dest = *--src; + case 3: *--dest = *--src; + case 2: *--dest = *--src; + case 1: *--dest = *--src; + } while (--i > 0); + } + } else if (src > dest) { + switch (j) { + case 0: do { + *dest++ = *src++; + case 7: *dest++ = *src++; + case 6: *dest++ = *src++; + case 5: *dest++ = *src++; + case 4: *dest++ = *src++; + case 3: *dest++ = *src++; + case 2: *dest++ = *src++; + case 1: *dest++ = *src++; + } while (--i > 0); + } + } +} + +static unsigned char a2i[128] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 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, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char odigits[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 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, +}; + +static unsigned char ddigits[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static unsigned char hdigits[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, + 0x7e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +#define isdigok(m, d) (m[(d) >> 3] & (1 << ((d) & 7))) + +/* + * Routine to convert an ASCII string into an unsigned long integer. + */ +unsigned long +#ifdef __STDC__ +_bdf_atoul(char *s, char **end, int base) +#else +_bdf_atoul(s, end, base) +char *s, **end; +int base; +#endif +{ + unsigned long v; + unsigned char *dmap; + + if (s == 0 || *s == 0) + return 0; + + /* + * Make sure the radix is something recognizable. Default to 10. + */ + switch (base) { + case 8: dmap = odigits; break; + case 16: dmap = hdigits; break; + default: base = 10; dmap = ddigits; break; + } + + /* + * Check for the special hex prefix. + */ + if (*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X')) { + base = 16; + dmap = hdigits; + s += 2; + } + + for (v = 0; isdigok(dmap, *s); s++) + v = (v * base) + a2i[(int) *s]; + + if (end != 0) + *end = s; + + return v; +} + +/* + * Routine to convert an ASCII string into an signed long integer. + */ +long +#ifdef __STDC__ +_bdf_atol(char *s, char **end, int base) +#else +_bdf_atol(s, end, base) +char *s, **end; +int base; +#endif +{ + long v, neg; + unsigned char *dmap; + + if (s == 0 || *s == 0) + return 0; + + /* + * Make sure the radix is something recognizable. Default to 10. + */ + switch (base) { + case 8: dmap = odigits; break; + case 16: dmap = hdigits; break; + default: base = 10; dmap = ddigits; break; + } + + /* + * Check for a minus sign. + */ + neg = 0; + if (*s == '-') { + s++; + neg = 1; + } + + /* + * Check for the special hex prefix. + */ + if (*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X')) { + base = 16; + dmap = hdigits; + s += 2; + } + + for (v = 0; isdigok(dmap, *s); s++) + v = (v * base) + a2i[(int) *s]; + + if (end != 0) + *end = s; + + return (!neg) ? v : -v; +} + +/* + * Routine to convert an ASCII string into an signed short integer. + */ +short +#ifdef __STDC__ +_bdf_atos(char *s, char **end, int base) +#else +_bdf_atos(s, end, base) +char *s, **end; +int base; +#endif +{ + short v, neg; + unsigned char *dmap; + + if (s == 0 || *s == 0) + return 0; + + /* + * Make sure the radix is something recognizable. Default to 10. + */ + switch (base) { + case 8: dmap = odigits; break; + case 16: dmap = hdigits; break; + default: base = 10; dmap = ddigits; break; + } + + /* + * Check for a minus. + */ + neg = 0; + if (*s == '-') { + s++; + neg = 1; + } + + /* + * Check for the special hex prefix. + */ + if (*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X')) { + base = 16; + dmap = hdigits; + s += 2; + } + + for (v = 0; isdigok(dmap, *s); s++) + v = (v * base) + a2i[(int) *s]; + + if (end != 0) + *end = s; + + return (!neg) ? v : -v; +} + +/* + * Routine to compare two glyphs by encoding so they can be sorted. + */ +static int +#ifdef __STDC__ +by_encoding(const void *a, const void *b) +#else +by_encoding(a, b) +char *a, *b; +#endif +{ + bdf_glyph_t *c1, *c2; + + c1 = (bdf_glyph_t *) a; + c2 = (bdf_glyph_t *) b; + if (c1->encoding < c2->encoding) + return -1; + else if (c1->encoding > c2->encoding) + return 1; + return 0; +} + +/************************************************************************** + * + * BDF font file parsing flags and functions. + * + **************************************************************************/ + +/* + * Parse flags. + */ +#define _BDF_START 0x0001 +#define _BDF_FONT_NAME 0x0002 +#define _BDF_SIZE 0x0004 +#define _BDF_FONT_BBX 0x0008 +#define _BDF_PROPS 0x0010 +#define _BDF_GLYPHS 0x0020 +#define _BDF_GLYPH 0x0040 +#define _BDF_ENCODING 0x0080 +#define _BDF_SWIDTH 0x0100 +#define _BDF_DWIDTH 0x0200 +#define _BDF_BBX 0x0400 +#define _BDF_BITMAP 0x0800 + +#define _BDF_SWIDTH_ADJ 0x1000 + +#define _BDF_GLYPH_BITS (_BDF_GLYPH|_BDF_ENCODING|_BDF_SWIDTH|\ + _BDF_DWIDTH|_BDF_BBX|_BDF_BITMAP) + +#define _BDF_GLYPH_WIDTH_CHECK 0x40000000 +#define _BDF_GLYPH_HEIGHT_CHECK 0x80000000 + +/* + * Auto correction messages. + */ +#define ACMSG1 "FONT_ASCENT property missing. Added \"FONT_ASCENT %hd\"." +#define ACMSG2 "FONT_DESCENT property missing. Added \"FONT_DESCENT %hd\"." +#define ACMSG3 "Font width != actual width. Old: %hd New: %hd." +#define ACMSG4 "Font left bearing != actual left bearing. Old: %hd New: %hd." +#define ACMSG5 "Font ascent != actual ascent. Old: %hd New: %hd." +#define ACMSG6 "Font descent != actual descent. Old: %hd New: %hd." +#define ACMSG7 "Font height != actual height. Old: %hd New: %hd." +#define ACMSG8 "Glyph scalable width (SWIDTH) adjustments made." +#define ACMSG9 "SWIDTH field missing at line %ld. Set automatically." +#define ACMSG10 "DWIDTH field missing at line %ld. Set to glyph width." +#define ACMSG11 "SIZE bits per pixel field adjusted to %hd." +#define ACMSG12 "Duplicate encoding %ld (%s) changed to unencoded." +#define ACMSG13 "Glyph %ld extra rows removed." +#define ACMSG14 "Glyph %ld extra columns removed." +#define ACMSG15 "Incorrect glyph count: %ld indicated but %ld found." + +/* + * Error messages. + */ +#define ERRMSG1 "[line %ld] Missing \"%s\" line." +#define ERRMSG2 "[line %ld] Font header corrupted or missing fields." +#define ERRMSG3 "[line %ld] Font glyphs corrupted or missing fields." + +void +#ifdef __STDC__ +_bdf_add_acmsg(bdf_font_t *font, char *msg, unsigned long len) +#else +_bdf_add_acmsg(font, msg, len) +bdf_font_t *font; +char *msg; +unsigned long len; +#endif +{ + char *cp; + + if (font->acmsgs_len == 0) + font->acmsgs = (char *) malloc(len + 1); + else + font->acmsgs = (char *) realloc(font->acmsgs, + font->acmsgs_len + len + 1); + + cp = font->acmsgs + font->acmsgs_len; + (void) memcpy(cp, msg, len); + cp += len; + *cp++ = '\n'; + font->acmsgs_len += len + 1; +} + +void +#ifdef __STDC__ +_bdf_add_comment(bdf_font_t *font, char *comment, unsigned long len) +#else +_bdf_add_comment(font, comment, len) +bdf_font_t *font; +char *comment; +unsigned long len; +#endif +{ + char *cp; + + if (font->comments_len == 0) + font->comments = (char *) malloc(len + 1); + else + font->comments = (char *) realloc(font->comments, + font->comments_len + len + 1); + + cp = font->comments + font->comments_len; + (void) memcpy(cp, comment, len); + cp += len; + *cp++ = '\n'; + font->comments_len += len + 1; +} + +/* + * Set the spacing from the font name if it exists, or set it to the default + * specified in the options. + */ +static void +#ifdef __STDC__ +_bdf_set_default_spacing(bdf_font_t *font, bdf_options_t *opts) +#else +_bdf_set_default_spacing(font, opts) +bdf_font_t *font; +bdf_options_t *opts; +#endif +{ + unsigned long len; + char name[128]; + _bdf_list_t list; + + if (font == 0 || font->name == 0 || font->name[0] == 0) + return; + + font->spacing = opts->font_spacing; + + len = (unsigned long) (strlen(font->name) + 1); + (void) memcpy(name, font->name, len); + list.size = list.used = 0; + _bdf_split("-", name, len, &list); + if (list.used == 15) { + switch (list.field[11][0]) { + case 'C': case 'c': font->spacing = BDF_CHARCELL; break; + case 'M': case 'm': font->spacing = BDF_MONOWIDTH; break; + case 'P': case 'p': font->spacing = BDF_PROPORTIONAL; break; + } + } + if (list.size > 0) + free((char *) list.field); +} + +/* + * Determine if the property is an atom or not. If it is, then clean it up so + * the double quotes are removed if they exist. + */ +static int +#ifdef __STDC__ +_bdf_is_atom(char *line, unsigned long linelen, char **name, char **value) +#else +_bdf_is_atom(line, linelen, name, value) +char *line; +unsigned long linelen; +char **name, **value; +#endif +{ + int hold; + char *sp, *ep; + bdf_property_t *p; + + *name = sp = ep = line; + while (*ep && *ep != ' ' && *ep != '\t') + ep++; + + hold = -1; + if (*ep) { + hold = *ep; + *ep = 0; + } + + p = bdf_get_property(sp); + + /* + * Restore the character that was saved before any return can happen. + */ + if (hold != -1) + *ep = hold; + + /* + * If the propert exists and is not an atom, just return here. + */ + if (p && p->format != BDF_ATOM) + return 0; + + /* + * The property is an atom. Trim all leading and trailing whitespace and + * double quotes for the atom value. + */ + sp = ep; + ep = line + linelen; + + /* + * Trim the leading whitespace if it exists. + */ + *sp++ = 0; + while (*sp && (*sp == ' ' || *sp == '\t')) + sp++; + + /* + * Trim the leading double quote if it exists. + */ + if (*sp == '"') + sp++; + *value = sp; + + /* + * Trim the trailing whitespace if it exists. + */ + while (ep > sp && (*(ep - 1) == ' ' || *(ep - 1) == '\t')) + *--ep = 0; + + /* + * Trim the trailing double quote if it exists. + */ + if (ep > sp && *(ep - 1) == '"') + *--ep = 0; + + return 1; +} + +static void +#ifdef __STDC__ +_bdf_add_property(bdf_font_t *font, char *name, char *value) +#else +_bdf_add_property(font, name, value) +bdf_font_t *font; +char *name, *value; +#endif +{ + unsigned long propid; + hashnode hn; + int len; + bdf_property_t *prop, *fp; + + /* + * First, check to see if the property already exists in the font. + */ + if ((hn = hash_lookup(name, (hashtable *) font->internal)) != 0) { + /* + * The property already exists in the font, so simply replace + * the value of the property with the current value. + */ + fp = font->props + (unsigned long) hn->data; + + switch (fp->format) { + case BDF_ATOM: + /* + * Delete the current atom if it exists. + */ + if (fp->value.atom != 0) + free(fp->value.atom); + + if (value == 0) + len = 1; + else + len = strlen(value) + 1; + if (len > 1) { + fp->value.atom = (char *) malloc(len); + (void) memcpy(fp->value.atom, value, len); + } else + fp->value.atom = 0; + break; + case BDF_INTEGER: + fp->value.int32 = _bdf_atol(value, 0, 10); + break; + case BDF_CARDINAL: + fp->value.card32 = _bdf_atoul(value, 0, 10); + break; + } + return; + } + + /* + * See if this property type exists yet or not. If not, create it. + */ + hn = hash_lookup(name, &proptbl); + if (hn == 0) { + bdf_create_property(name, BDF_ATOM); + hn = hash_lookup(name, &proptbl); + } + + /* + * Allocate another property if this is overflow. + */ + if (font->props_used == font->props_size) { + if (font->props_size == 0) + font->props = (bdf_property_t *) malloc(sizeof(bdf_property_t)); + else + font->props = (bdf_property_t *) + realloc((char *) font->props, sizeof(bdf_property_t) * + (font->props_size + 1)); + fp = font->props + font->props_size; + (void) memset((char *) fp, 0, sizeof(bdf_property_t)); + font->props_size++; + } + + propid = (unsigned long) hn->data; + if (propid >= _num_bdf_properties) + prop = user_props + (propid - _num_bdf_properties); + else + prop = _bdf_properties + propid; + + fp = font->props + font->props_used; + + fp->name = prop->name; + fp->format = prop->format; + fp->builtin = prop->builtin; + + switch (prop->format) { + case BDF_ATOM: + if (value == 0) + len = 1; + else + len = strlen(value) + 1; + if (len > 1) { + fp->value.atom = (char *) malloc(len); + (void) memcpy(fp->value.atom, value, len); + } else + fp->value.atom = 0; + break; + case BDF_INTEGER: + fp->value.int32 = _bdf_atol(value, 0, 10); + break; + case BDF_CARDINAL: + fp->value.card32 = _bdf_atoul(value, 0, 10); + break; + } + + /* + * If the property happens to be a comment, then it doesn't need + * to be added to the internal hash table. + */ + if (memcmp(name, "COMMENT", 7) != 0) + /* + * Add the property to the font property table. + */ + hash_insert(fp->name, (void *) font->props_used, + (hashtable *) font->internal); + + font->props_used++; + + /* + * Some special cases need to be handled here. The DEFAULT_CHAR property + * needs to be located if it exists in the property list, the FONT_ASCENT + * and FONT_DESCENT need to be assigned if they are present, and the + * SPACING property should override the default spacing. + */ + if (memcmp(name, "DEFAULT_CHAR", 12) == 0) + font->default_glyph = fp->value.int32; + else if (memcmp(name, "FONT_ASCENT", 11) == 0) + font->font_ascent = fp->value.int32; + else if (memcmp(name, "FONT_DESCENT", 12) == 0) + font->font_descent = fp->value.int32; + else if (memcmp(name, "SPACING", 7) == 0) { + if (fp->value.atom[0] == 'p' || fp->value.atom[0] == 'P') + font->spacing = BDF_PROPORTIONAL; + else if (fp->value.atom[0] == 'm' || fp->value.atom[0] == 'M') + font->spacing = BDF_MONOWIDTH; + else if (fp->value.atom[0] == 'c' || fp->value.atom[0] == 'C') + font->spacing = BDF_CHARCELL; + } +} + +/* + * Actually parse the glyph info and bitmaps. + */ +static int +#ifdef __STDC__ +_bdf_parse_glyphs(char *line, unsigned long linelen, unsigned long lineno, + void *call_data, void *client_data) +#else +_bdf_parse_glyphs(line, linelen, lineno, call_data, client_data) +char *line; +unsigned long linelen, lineno; +void *call_data, *client_data; +#endif +{ + int c; + char *s; + unsigned char *bp; + unsigned long i, slen, nibbles; + double ps, rx, dw, sw; + _bdf_line_func_t *next; + _bdf_parse_t *p; + bdf_glyph_t *glyph; + bdf_font_t *font; + char nbuf[128]; + + next = (_bdf_line_func_t *) call_data; + p = (_bdf_parse_t *) client_data; + + font = p->font; + + /* + * Check for a comment. + */ + if (memcmp(line, "COMMENT", 7) == 0) { + linelen -= 7; + s = line + 7; + if (*s != 0) { + s++; + linelen--; + } + _bdf_add_comment(p->font, s, linelen); + return 0; + } + + /* + * The very first thing expected is the number of glyphs. + */ + if (!(p->flags & _BDF_GLYPHS)) { + if (memcmp(line, "CHARS", 5) != 0) { + sprintf(nbuf, ERRMSG1, lineno, "CHARS"); + _bdf_add_acmsg(p->font, nbuf, strlen(nbuf)); + return BDF_MISSING_CHARS; + } + _bdf_split(" +", line, linelen, &p->list); + p->cnt = font->glyphs_size = _bdf_atoul(p->list.field[1], 0, 10); + + /* + * Make sure the number of glyphs is non-zero. + */ + if (p->cnt == 0) + font->glyphs_size = 64; + + font->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * + font->glyphs_size); + + /* + * Set up the callback to indicate the glyph loading is about to + * begin. + */ + if (p->callback != 0) { + p->cb.reason = BDF_LOAD_START; + p->cb.total = p->cnt; + p->cb.current = 0; + (*p->callback)(&p->cb, p->client_data); + } + p->flags |= _BDF_GLYPHS; + return 0; + } + + /* + * Check for the ENDFONT field. + */ + if (memcmp(line, "ENDFONT", 7) == 0) { + /* + * Sort the glyphs by encoding. + */ + qsort((char *) font->glyphs, font->glyphs_used, sizeof(bdf_glyph_t), + by_encoding); + + p->flags &= ~_BDF_START; + return 0; + } + + /* + * Check for the ENDCHAR field. + */ + if (memcmp(line, "ENDCHAR", 7) == 0) { + /* + * Set up and call the callback if it was passed. + */ + if (p->callback != 0) { + p->cb.reason = BDF_LOADING; + p->cb.total = font->glyphs_size; + p->cb.current = font->glyphs_used; + (*p->callback)(&p->cb, p->client_data); + } + p->glyph_enc = 0; + p->flags &= ~_BDF_GLYPH_BITS; + return 0; + } + + /* + * Check to see if a glyph is being scanned but should be ignored + * because it is an unencoded glyph. + */ + if ((p->flags & _BDF_GLYPH) && + p->glyph_enc == -1 && p->opts->keep_unencoded == 0) + return 0; + + /* + * Check for the STARTCHAR field. + */ + if (memcmp(line, "STARTCHAR", 9) == 0) { + /* + * Set the character name in the parse info first until the + * encoding can be checked for an unencoded character. + */ + if (p->glyph_name != 0) + free(p->glyph_name); + _bdf_split(" +", line, linelen, &p->list); + _bdf_shift(1, &p->list); + s = _bdf_join(' ', &slen, &p->list); + p->glyph_name = (char *) malloc(slen + 1); + (void) memcpy(p->glyph_name, s, slen + 1); + p->flags |= _BDF_GLYPH; + return 0; + } + + /* + * Check for the ENCODING field. + */ + if (memcmp(line, "ENCODING", 8) == 0) { + if (!(p->flags & _BDF_GLYPH)) { + /* + * Missing STARTCHAR field. + */ + sprintf(nbuf, ERRMSG1, lineno, "STARTCHAR"); + _bdf_add_acmsg(font, nbuf, strlen(nbuf)); + return BDF_MISSING_STARTCHAR; + } + _bdf_split(" +", line, linelen, &p->list); + p->glyph_enc = _bdf_atol(p->list.field[1], 0, 10); + + /* + * Check to see if this encoding has already been encountered. If it + * has then change it to unencoded so it gets added if indicated. + */ + if (p->glyph_enc >= 0) { + if (_bdf_glyph_modified(p->have, p->glyph_enc)) { + /* + * Add a message saying a glyph has been moved to the + * unencoded area. + */ + sprintf(nbuf, ACMSG12, p->glyph_enc, p->glyph_name); + _bdf_add_acmsg(font, nbuf, strlen(nbuf)); + p->glyph_enc = -1; + font->modified = 1; + } else + _bdf_set_glyph_modified(p->have, p->glyph_enc); + } + + if (p->glyph_enc >= 0) { + /* + * Make sure there are enough glyphs allocated in case the + * number of characters happen to be wrong. + */ + if (font->glyphs_used == font->glyphs_size) { + font->glyphs = (bdf_glyph_t *) + realloc((char *) font->glyphs, + sizeof(bdf_glyph_t) * (font->glyphs_size + 64)); + (void) memset((char *) (font->glyphs + font->glyphs_size), + 0, sizeof(bdf_glyph_t) << 6); + font->glyphs_size += 64; + } + + glyph = font->glyphs + font->glyphs_used++; + glyph->name = p->glyph_name; + glyph->encoding = p->glyph_enc; + + /* + * Reset the initial glyph info. + */ + p->glyph_name = 0; + } else { + /* + * Unencoded glyph. Check to see if it should be added or not. + */ + if (p->opts->keep_unencoded != 0) { + /* + * Allocate the next unencoded glyph. + */ + if (font->unencoded_used == font->unencoded_size) { + if (font->unencoded_size == 0) + font->unencoded = (bdf_glyph_t *) + malloc(sizeof(bdf_glyph_t) << 2); + else + font->unencoded = (bdf_glyph_t *) + realloc((char *) font->unencoded, + sizeof(bdf_glyph_t) * + (font->unencoded_size + 4)); + font->unencoded_size += 4; + } + + glyph = font->unencoded + font->unencoded_used; + glyph->name = p->glyph_name; + glyph->encoding = font->unencoded_used++; + } else + /* + * Free up the glyph name if the unencoded shouldn't be + * kept. + */ + free(p->glyph_name); + + p->glyph_name = 0; + } + + /* + * Clear the flags that might be added when width and height are + * checked for consistency. + */ + p->flags &= ~(_BDF_GLYPH_WIDTH_CHECK|_BDF_GLYPH_HEIGHT_CHECK); + + p->flags |= _BDF_ENCODING; + return 0; + } + + /* + * Point at the glyph being constructed. + */ + if (p->glyph_enc == -1) + glyph = font->unencoded + (font->unencoded_used - 1); + else + glyph = font->glyphs + (font->glyphs_used - 1); + + /* + * Check to see if a bitmap is being constructed. + */ + if (p->flags & _BDF_BITMAP) { + /* + * If there are more rows than are specified in the glyph metrics, + * ignore the remaining lines. + */ + if (p->row >= glyph->bbx.height) { + if (!(p->flags & _BDF_GLYPH_HEIGHT_CHECK)) { + sprintf(nbuf, ACMSG13, glyph->encoding); + _bdf_add_acmsg(font, nbuf, strlen(nbuf)); + p->flags |= _BDF_GLYPH_HEIGHT_CHECK; + font->modified = 1; + } + return 0; + } + + /* + * Only collect the number of nibbles indicated by the glyph metrics. + * If there are more columns, they are simply ignored. + */ + nibbles = p->bpr << 1; + bp = glyph->bitmap + (p->row * p->bpr); + for (i = 0, *bp = 0; i < nibbles; i++) { + c = line[i]; + *bp = (*bp << 4) + a2i[c]; + if (i + 1 < nibbles && (i & 1)) + *++bp = 0; + } + + /* + * If any line has extra columns, indicate they have been removed. + */ + if ((line[nibbles] == '0' || a2i[(int) line[nibbles]] != 0) && + !(p->flags & _BDF_GLYPH_WIDTH_CHECK)) { + sprintf(nbuf, ACMSG14, glyph->encoding); + _bdf_add_acmsg(font, nbuf, strlen(nbuf)); + p->flags |= _BDF_GLYPH_WIDTH_CHECK; + font->modified = 1; + } + + p->row++; + return 0; + } + + /* + * Expect the SWIDTH (scalable width) field next. + */ + if (memcmp(line, "SWIDTH", 6) == 0) { + if (!(p->flags & _BDF_ENCODING)) { + /* + * Missing ENCODING field. + */ + sprintf(nbuf, ERRMSG1, lineno, "ENCODING"); + _bdf_add_acmsg(font, nbuf, strlen(nbuf)); + return BDF_MISSING_ENCODING; + } + _bdf_split(" +", line, linelen, &p->list); + glyph->swidth = _bdf_atoul(p->list.field[1], 0, 10); + p->flags |= _BDF_SWIDTH; + return 0; + } + + /* + * Expect the DWIDTH (scalable width) field next. + */ + if (memcmp(line, "DWIDTH", 6) == 0) { + _bdf_split(" +", line, linelen, &p->list); + glyph->dwidth = _bdf_atoul(p->list.field[1], 0, 10); + + if (!(p->flags & _BDF_SWIDTH)) { + /* + * Missing SWIDTH field. Add an auto correction message and set + * the scalable width from the device width. + */ + sprintf(nbuf, ACMSG9, lineno); + _bdf_add_acmsg(font, nbuf, strlen(nbuf)); + ps = (double) font->point_size; + rx = (double) font->resolution_x; + dw = (double) glyph->dwidth; + glyph->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx)); + } + + p->flags |= _BDF_DWIDTH; + return 0; + } + + /* + * Expect the BBX field next. + */ + if (memcmp(line, "BBX", 3) == 0) { + _bdf_split(" +", line, linelen, &p->list); + glyph->bbx.width = _bdf_atos(p->list.field[1], 0, 10); + glyph->bbx.height = _bdf_atos(p->list.field[2], 0, 10); + glyph->bbx.x_offset = _bdf_atos(p->list.field[3], 0, 10); + glyph->bbx.y_offset = _bdf_atos(p->list.field[4], 0, 10); + + /* + * Generate the ascent and descent of the character. + */ + glyph->bbx.ascent = glyph->bbx.height + glyph->bbx.y_offset; + glyph->bbx.descent = -glyph->bbx.y_offset; + + /* + * Determine the overall font bounding box as the characters are + * loaded so corrections can be done later if indicated. + */ + p->maxas = MAX(glyph->bbx.ascent, p->maxas); + p->maxds = MAX(glyph->bbx.descent, p->maxds); + p->rbearing = glyph->bbx.width + glyph->bbx.x_offset; + p->maxrb = MAX(p->rbearing, p->maxrb); + p->minlb = MIN(glyph->bbx.x_offset, p->minlb); + p->maxlb = MAX(glyph->bbx.x_offset, p->maxlb); + + if (!(p->flags & _BDF_DWIDTH)) { + /* + * Missing DWIDTH field. Add an auto correction message and set + * the device width to the glyph width. + */ + sprintf(nbuf, ACMSG10, lineno); + _bdf_add_acmsg(font, nbuf, strlen(nbuf)); + glyph->dwidth = glyph->bbx.width; + } + + /* + * If the BDF_CORRECT_METRICS flag is set, then adjust the SWIDTH + * value if necessary. + */ + if (p->opts->correct_metrics != 0) { + /* + * Determine the point size of the glyph. + */ + ps = (double) font->point_size; + rx = (double) font->resolution_x; + dw = (double) glyph->dwidth; + sw = (unsigned short) ((dw * 72000.0) / (ps * rx)); + + if (sw != glyph->swidth) { + glyph->swidth = (unsigned short) sw; + if (p->glyph_enc == -1) + _bdf_set_glyph_modified(font->umod, + font->unencoded_used - 1); + else + _bdf_set_glyph_modified(font->nmod, glyph->encoding); + p->flags |= _BDF_SWIDTH_ADJ; + font->modified = 1; + } + } + p->flags |= _BDF_BBX; + return 0; + } + + /* + * And finally, gather up the bitmap. + */ + if (memcmp(line, "BITMAP", 6) == 0) { + if (!(p->flags & _BDF_BBX)) { + /* + * Missing BBX field. + */ + sprintf(nbuf, ERRMSG1, lineno, "BBX"); + _bdf_add_acmsg(font, nbuf, strlen(nbuf)); + return BDF_MISSING_BBX; + } + /* + * Allocate enough space for the bitmap. + */ + p->bpr = ((glyph->bbx.width * p->font->bpp) + 7) >> 3; + glyph->bytes = p->bpr * glyph->bbx.height; + glyph->bitmap = (unsigned char *) malloc(glyph->bytes); + p->row = 0; + p->flags |= _BDF_BITMAP; + return 0; + } + + return BDF_INVALID_LINE; +} + +/* + * Load the font properties. + */ +static int +#ifdef __STDC__ +_bdf_parse_properties(char *line, unsigned long linelen, unsigned long lineno, + void *call_data, void *client_data) +#else +_bdf_parse_properties(line, linelen, lineno, call_data, client_data) +char *line; +unsigned long linelen, lineno; +void *call_data, *client_data; +#endif +{ + unsigned long vlen; + _bdf_line_func_t *next; + _bdf_parse_t *p; + char *name, *value, nbuf[128]; + + next = (_bdf_line_func_t *) call_data; + p = (_bdf_parse_t *) client_data; + + /* + * Check for the end of the properties. + */ + if (memcmp(line, "ENDPROPERTIES", 13) == 0) { + /* + * If the FONT_ASCENT or FONT_DESCENT properties have not been + * encountered yet, then make sure they are added as properties and + * make sure they are set from the font bounding box info. + * + * This is *always* done regardless of the options, because X11 + * requires these two fields to compile fonts. + */ + if (bdf_get_font_property(p->font, "FONT_ASCENT") == 0) { + p->font->font_ascent = p->font->bbx.ascent; + sprintf(nbuf, "%hd", p->font->bbx.ascent); + _bdf_add_property(p->font, "FONT_ASCENT", nbuf); + sprintf(nbuf, ACMSG1, p->font->bbx.ascent); + _bdf_add_acmsg(p->font, nbuf, strlen(nbuf)); + p->font->modified = 1; + } + if (bdf_get_font_property(p->font, "FONT_DESCENT") == 0) { + p->font->font_descent = p->font->bbx.descent; + sprintf(nbuf, "%hd", p->font->bbx.descent); + _bdf_add_property(p->font, "FONT_DESCENT", nbuf); + sprintf(nbuf, ACMSG2, p->font->bbx.descent); + _bdf_add_acmsg(p->font, nbuf, strlen(nbuf)); + p->font->modified = 1; + } + p->flags &= ~_BDF_PROPS; + *next = _bdf_parse_glyphs; + return 0; + } + + /* + * Ignore the _XFREE86_GLYPH_RANGES properties. + */ + if (memcmp(line, "_XFREE86_GLYPH_RANGES", 21) == 0) + return 0; + + /* + * Handle COMMENT fields and properties in a special way to preserve + * the spacing. + */ + if (memcmp(line, "COMMENT", 7) == 0) { + name = value = line; + value += 7; + if (*value) + *value++ = 0; + _bdf_add_property(p->font, name, value); + } else if (_bdf_is_atom(line, linelen, &name, &value)) + _bdf_add_property(p->font, name, value); + else { + _bdf_split(" +", line, linelen, &p->list); + name = p->list.field[0]; + _bdf_shift(1, &p->list); + value = _bdf_join(' ', &vlen, &p->list); + _bdf_add_property(p->font, name, value); + } + + return 0; +} + +/* + * Load the font header. + */ +static int +#ifdef __STDC__ +_bdf_parse_start(char *line, unsigned long linelen, unsigned long lineno, + void *call_data, void *client_data) +#else +_bdf_parse_start(line, linelen, lineno, call_data, client_data) +char *line; +unsigned long linelen, lineno; +void *call_data, *client_data; +#endif +{ + unsigned long slen; + _bdf_line_func_t *next; + _bdf_parse_t *p; + bdf_font_t *font; + char *s, nbuf[128]; + + next = (_bdf_line_func_t *) call_data; + p = (_bdf_parse_t *) client_data; + + /* + * Check for a comment. This is done to handle those fonts that have + * comments before the STARTFONT line for some reason. + */ + if (memcmp(line, "COMMENT", 7) == 0) { + if (p->opts->keep_comments != 0 && p->font != 0) { + linelen -= 7; + s = line + 7; + if (*s != 0) { + s++; + linelen--; + } + _bdf_add_comment(p->font, s, linelen); + } + return 0; + } + + if (!(p->flags & _BDF_START)) { + if (memcmp(line, "STARTFONT", 9) != 0) + /* + * No STARTFONT field is a good indication of a problem. + */ + return BDF_MISSING_START; + p->flags = _BDF_START; + p->font = font = (bdf_font_t *) calloc(1, sizeof(bdf_font_t)); + p->font->internal = (void *) malloc(sizeof(hashtable)); + hash_init((hashtable *) p->font->internal); + p->font->spacing = p->opts->font_spacing; + p->font->default_glyph = -1; + return 0; + } + + /* + * Check for the start of the properties. + */ + if (memcmp(line, "STARTPROPERTIES", 15) == 0) { + _bdf_split(" +", line, linelen, &p->list); + p->cnt = p->font->props_size = _bdf_atoul(p->list.field[1], 0, 10); + p->font->props = (bdf_property_t *) + malloc(sizeof(bdf_property_t) * p->cnt); + p->flags |= _BDF_PROPS; + *next = _bdf_parse_properties; + return 0; + } + + /* + * Check for the FONTBOUNDINGBOX field. + */ + if (memcmp(line, "FONTBOUNDINGBOX", 15) == 0) { + if (!(p->flags & _BDF_SIZE)) { + /* + * Missing the SIZE field. + */ + sprintf(nbuf, ERRMSG1, lineno, "SIZE"); + _bdf_add_acmsg(p->font, nbuf, strlen(nbuf)); + return BDF_MISSING_SIZE; + } + _bdf_split(" +", line, linelen, &p->list); + p->font->bbx.width = _bdf_atos(p->list.field[1], 0, 10); + p->font->bbx.height = _bdf_atos(p->list.field[2], 0, 10); + p->font->bbx.x_offset = _bdf_atos(p->list.field[3], 0, 10); + p->font->bbx.y_offset = _bdf_atos(p->list.field[4], 0, 10); + p->font->bbx.ascent = p->font->bbx.height + p->font->bbx.y_offset; + p->font->bbx.descent = -p->font->bbx.y_offset; + p->flags |= _BDF_FONT_BBX; + return 0; + } + + /* + * The next thing to check for is the FONT field. + */ + if (memcmp(line, "FONT", 4) == 0) { + _bdf_split(" +", line, linelen, &p->list); + _bdf_shift(1, &p->list); + s = _bdf_join(' ', &slen, &p->list); + p->font->name = (char *) malloc(slen + 1); + (void) memcpy(p->font->name, s, slen + 1); + /* + * If the font name is an XLFD name, set the spacing to the one in the + * font name. If there is no spacing fall back on the default. + */ + _bdf_set_default_spacing(p->font, p->opts); + p->flags |= _BDF_FONT_NAME; + return 0; + } + + /* + * Check for the SIZE field. + */ + if (memcmp(line, "SIZE", 4) == 0) { + if (!(p->flags & _BDF_FONT_NAME)) { + /* + * Missing the FONT field. + */ + sprintf(nbuf, ERRMSG1, lineno, "FONT"); + _bdf_add_acmsg(p->font, nbuf, strlen(nbuf)); + return BDF_MISSING_FONTNAME; + } + _bdf_split(" +", line, linelen, &p->list); + p->font->point_size = _bdf_atoul(p->list.field[1], 0, 10); + p->font->resolution_x = _bdf_atoul(p->list.field[2], 0, 10); + p->font->resolution_y = _bdf_atoul(p->list.field[3], 0, 10); + + /* + * Check for the bits per pixel field. + */ + if (p->list.used == 5) { + p->font->bpp = _bdf_atos(p->list.field[4], 0, 10); + if (p->font->bpp > 1 && (p->font->bpp & 1)) { + /* + * Move up to the next bits per pixel value if an odd number + * is encountered. + */ + p->font->bpp++; + if (p->font->bpp <= 4) { + sprintf(nbuf, ACMSG11, p->font->bpp); + _bdf_add_acmsg(p->font, nbuf, strlen(nbuf)); + } + } + if (p->font->bpp > 4) { + sprintf(nbuf, ACMSG11, p->font->bpp); + _bdf_add_acmsg(p->font, nbuf, strlen(nbuf)); + p->font->bpp = 4; + } + } else + p->font->bpp = 1; + + p->flags |= _BDF_SIZE; + return 0; + } + + return BDF_INVALID_LINE; +} + +/************************************************************************** + * + * API. + * + **************************************************************************/ + +void +#ifdef __STDC__ +bdf_setup(void) +#else +bdf_setup() +#endif +{ + unsigned long i; + bdf_property_t *prop; + + hash_init(&proptbl); + for (i = 0, prop = _bdf_properties; i < _num_bdf_properties; i++, prop++) + hash_insert(prop->name, (void *) i, &proptbl); +} + +void +#ifdef __STDC__ +bdf_cleanup(void) +#else +bdf_cleanup() +#endif +{ + unsigned long i; + bdf_property_t *prop; + + hash_free(&proptbl); + + /* + * Free up the user defined properties. + */ + for (prop = user_props, i = 0; i < nuser_props; i++, prop++) { + free(prop->name); + if (prop->format == BDF_ATOM && prop->value.atom != 0) + free(prop->value.atom); + } + if (nuser_props > 0) + free((char *) user_props); + + _bdf_glyph_name_cleanup(); +} + +bdf_font_t * +#ifdef __STDC__ +bdf_load_font(FILE *in, bdf_options_t *opts, bdf_callback_t callback, + void *data) +#else +bdf_load_font(in, opts, callback, data) +FILE *in; +bdf_options_t *opts; +bdf_callback_t callback; +void *data; +#endif +{ + int n; + unsigned long lineno; + char msgbuf[128]; + _bdf_parse_t p; + + (void) memset((char *) &p, 0, sizeof(_bdf_parse_t)); + p.opts = (opts != 0) ? opts : &_bdf_opts; + p.minlb = 32767; + p.callback = callback; + p.client_data = data; + n = _bdf_readlines(fileno(in), _bdf_parse_start, (void *) &p, &lineno); + + if (p.font != 0) { + /* + * If the font is not proportional, set the fonts monowidth + * field to the width of the font bounding box. + */ + if (p.font->spacing != BDF_PROPORTIONAL) + p.font->monowidth = p.font->bbx.width; + + /* + * If the number of glyphs loaded is not that of the original count, + * indicate the difference. + */ + if (p.cnt != p.font->glyphs_used + p.font->unencoded_used) { + sprintf(msgbuf, ACMSG15, p.cnt, + p.font->glyphs_used + p.font->unencoded_used); + _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf)); + p.font->modified = 1; + } + + /* + * Once the font has been loaded, adjust the overall font metrics if + * necessary. + */ + if (p.opts->correct_metrics != 0 && + (p.font->glyphs_used > 0 || p.font->unencoded_used > 0)) { + if (p.maxrb - p.minlb != p.font->bbx.width) { + sprintf(msgbuf, ACMSG3, p.font->bbx.width, p.maxrb - p.minlb); + _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf)); + p.font->bbx.width = p.maxrb - p.minlb; + p.font->modified = 1; + } + if (p.font->bbx.x_offset != p.minlb) { + sprintf(msgbuf, ACMSG4, p.font->bbx.x_offset, p.minlb); + _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf)); + p.font->bbx.x_offset = p.minlb; + p.font->modified = 1; + } + if (p.font->bbx.ascent != p.maxas) { + sprintf(msgbuf, ACMSG5, p.font->bbx.ascent, p.maxas); + _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf)); + p.font->bbx.ascent = p.maxas; + p.font->modified = 1; + } + if (p.font->bbx.descent != p.maxds) { + sprintf(msgbuf, ACMSG6, p.font->bbx.descent, p.maxds); + _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf)); + p.font->bbx.descent = p.maxds; + p.font->bbx.y_offset = -p.maxds; + p.font->modified = 1; + } + if (p.maxas + p.maxds != p.font->bbx.height) { + sprintf(msgbuf, ACMSG7, p.font->bbx.height, p.maxas + p.maxds); + _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf)); + } + p.font->bbx.height = p.maxas + p.maxds; + + if (p.flags & _BDF_SWIDTH_ADJ) + _bdf_add_acmsg(p.font, ACMSG8, strlen(ACMSG8)); + } + } + + /* + * Last, if an error happened during loading, handle the messages. + */ + if (n < 0 && callback != 0) { + /* + * An error was returned. Alert the client. + */ + p.cb.reason = BDF_ERROR; + p.cb.errlineno = lineno; + (*callback)(&p.cb, data); + } else if (p.flags & _BDF_START) { + if (p.font != 0) { + /* + * The ENDFONT field was never reached or did not exist. + */ + if (!(p.flags & _BDF_GLYPHS)) + /* + * Error happened while parsing header. + */ + sprintf(msgbuf, ERRMSG2, lineno); + else + /* + * Error happened when parsing glyphs. + */ + sprintf(msgbuf, ERRMSG3, lineno); + + _bdf_add_acmsg(p.font, msgbuf, strlen(msgbuf)); + } + + if (callback != 0) { + p.cb.reason = BDF_ERROR; + p.cb.errlineno = lineno; + (*callback)(&p.cb, data); + } + } else if (callback != 0) { + /* + * This forces the progress bar to always finish. + */ + p.cb.current = p.cb.total; + (*p.callback)(&p.cb, p.client_data); + } + + /* + * Free up the list used during the parsing. + */ + if (p.list.size > 0) + free((char *) p.list.field); + + if (p.font != 0) { + /* + * Make sure the comments are NULL terminated if they exist. + */ + if (p.font->comments_len > 0) { + p.font->comments = (char *) realloc(p.font->comments, + p.font->comments_len + 1); + p.font->comments[p.font->comments_len] = 0; + } + + /* + * Make sure the auto-correct messages are NULL terminated if they + * exist. + */ + if (p.font->acmsgs_len > 0) { + p.font->acmsgs = (char *) realloc(p.font->acmsgs, + p.font->acmsgs_len + 1); + p.font->acmsgs[p.font->acmsgs_len] = 0; + } + } + + return p.font; +} + +#ifdef HAVE_HBF +static int +#ifdef __STDC__ +_bdf_parse_hbf_header(char *line, unsigned long linelen, unsigned long lineno, + void *call_data, void *client_data) +#else +_bdf_parse_hbf_header(line, linelen, lineno, call_data, client_data) +char *line; +unsigned long linelen, lineno; +void *call_data, *client_data; +#endif +{ + unsigned long vlen; + char *name, *value; + _bdf_parse_t *p; + _bdf_line_func_t *next; + char nbuf[24]; + + next = (_bdf_line_func_t *) call_data; + p = (_bdf_parse_t *) client_data; + + /* + * Check for comments. + */ + if (memcmp(line, "COMMENT", 7) == 0) { + if (p->opts->keep_comments != 0 && p->font != 0) { + name = line; + value = name + 7; + vlen = linelen - 7; + if (*value) { + *value++ = 0; + vlen--; + } + /* + * If the properties are being parsed, add the comment as a + * property. Otherwise, simply add the comment in the normal + * fashion. + */ + if (p->flags & _BDF_PROPS) + _bdf_add_property(p->font, name, value); + else + _bdf_add_comment(p->font, value, vlen); + } + return 0; + } + + if (!(p->flags & _BDF_START)) { + if (memcmp(line, "HBF_START_FONT", 14) != 0) + return -1; + p->flags = _BDF_START; + p->font = (bdf_font_t *) calloc(1, sizeof(bdf_font_t)); + /* + * HBF fonts are always assumed to be 1 bit per pixel. + */ + p->font->bpp = 1; + p->font->internal = (void *) malloc(sizeof(hashtable)); + hash_init((hashtable *) p->font->internal); + p->font->hbf = 1; + p->font->spacing = p->opts->font_spacing; + p->font->default_glyph = -1; + return 0; + } + + /* + * Check for the HBF_END_FONT field. + */ + if (memcmp(line, "HBF_END_FONT", 12) == 0) + /* + * Need to perform some checks here to see whether some fields are + * missing or not. + */ + return 0; + + /* + * Check for HBF keywords which will be added as comments. These should + * never occur in the properties list. Assume they won't. + */ + if (memcmp(line, "HBF_", 4) == 0) { + if (p->opts->keep_comments != 0) + _bdf_add_comment(p->font, line, linelen); + return 0; + } + + if (!(p->flags & _BDF_PROPS)) { + /* + * Check for the start of the properties. + */ + if (memcmp(line, "STARTPROPERTIES", 15) == 0) { + _bdf_split(" +", line, linelen, &p->list); + p->cnt = p->font->props_size = _bdf_atoul(p->list.field[1], 0, 10); + p->font->props = (bdf_property_t *) + malloc(sizeof(bdf_property_t) * p->cnt); + p->flags |= _BDF_PROPS; + return 0; + } + + /* + * Check for the CHARS field. + */ + if (memcmp(line, "CHARS", 5) == 0) { + _bdf_split(" +", line, linelen, &p->list); + p->cnt = p->font->glyphs_size = + _bdf_atoul(p->list.field[1], 0, 10); + p->font->glyphs = (bdf_glyph_t *) + malloc(sizeof(bdf_glyph_t) * p->cnt); + return 0; + } + + /* + * Check for the FONTBOUNDINGBOX field. + */ + if (memcmp(line, "FONTBOUNDINGBOX", 15) == 0) { + if (!(p->flags & (_BDF_START|_BDF_FONT_NAME|_BDF_SIZE))) + return -1; + _bdf_split(" +", line, linelen, &p->list); + p->font->bbx.width = _bdf_atos(p->list.field[1], 0, 10); + p->font->bbx.height = _bdf_atos(p->list.field[2], 0, 10); + p->font->bbx.x_offset = _bdf_atos(p->list.field[3], 0, 10); + p->font->bbx.y_offset = _bdf_atos(p->list.field[4], 0, 10); + p->font->bbx.ascent = p->font->bbx.height + p->font->bbx.y_offset; + p->font->bbx.descent = -p->font->bbx.y_offset; + p->flags |= _BDF_FONT_BBX; + return 0; + } + + /* + * The next thing to check for is the FONT field. + */ + if (memcmp(line, "FONT", 4) == 0) { + if (!(p->flags & _BDF_START)) + return -1; + _bdf_split(" +", line, linelen, &p->list); + _bdf_shift(1, &p->list); + value = _bdf_join(' ', &vlen, &p->list); + p->font->name = (char *) malloc(vlen + 1); + (void) memcpy(p->font->name, value, vlen + 1); + /* + * If the font name is an XLFD name, set the spacing to the one in + * the font name. If there is no spacing fall back on the + * default. + */ + _bdf_set_default_spacing(p->font, p->opts); + p->flags |= _BDF_FONT_NAME; + return 0; + } + + /* + * Check for the SIZE field. + */ + if (memcmp(line, "SIZE", 4) == 0) { + if (!(p->flags & (_BDF_START|_BDF_FONT_NAME))) + return -1; + _bdf_split(" +", line, linelen, &p->list); + p->font->point_size = _bdf_atoul(p->list.field[1], 0, 10); + p->font->resolution_x = _bdf_atoul(p->list.field[2], 0, 10); + p->font->resolution_y = _bdf_atoul(p->list.field[3], 0, 10); + p->flags |= _BDF_SIZE; + return 0; + } + } else { + /* + * Check for the end of the properties. + */ + if (memcmp(line, "ENDPROPERTIES", 13) == 0) { + /* + * If the FONT_ASCENT or FONT_DESCENT properties have not been + * encountered yet, then make sure they are added as properties and + * make sure they are set from the font bounding box info. + * + * This is *always* done regardless of the options, because X11 + * requires these two fields to compile fonts. + */ + if (bdf_get_font_property(p->font, "FONT_ASCENT") == 0) { + p->font->font_ascent = p->font->bbx.ascent; + sprintf(nbuf, "%hd", p->font->bbx.ascent); + _bdf_add_property(p->font, "FONT_ASCENT", nbuf); + sprintf(nbuf, ACMSG1, p->font->bbx.ascent); + _bdf_add_acmsg(p->font, nbuf, strlen(nbuf)); + p->font->modified = 1; + } + if (bdf_get_font_property(p->font, "FONT_DESCENT") == 0) { + p->font->font_descent = p->font->bbx.descent; + sprintf(nbuf, "%hd", p->font->bbx.descent); + _bdf_add_property(p->font, "FONT_DESCENT", nbuf); + sprintf(nbuf, ACMSG2, p->font->bbx.descent); + _bdf_add_acmsg(p->font, nbuf, strlen(nbuf)); + p->font->modified = 1; + } + p->flags &= ~_BDF_PROPS; + return 0; + } + + /* + * Handle the next thing in the usual property fashion. + */ + if (_bdf_is_atom(line, linelen, &name, &value)) + _bdf_add_property(p->font, name, value); + else { + _bdf_split(" +", line, linelen, &p->list); + name = p->list.field[0]; + _bdf_shift(1, &p->list); + value = _bdf_join(' ', &vlen, &p->list); + _bdf_add_property(p->font, name, value); + } + return 0; + } + + /* + * Anything else is an error. + */ + return -1; +} + +#ifdef __STDC__ +#define CONST const +#else +#define CONST +#endif + +static void +#ifdef __STDC__ +_bdf_add_hbf_glyph(HBF *hbf, unsigned int code, void *callback_data) +#else +_bdf_add_hbf_glyph(hbf, code, callback_data) +HBF *hbf; +unsigned int code; +void *callback_data; +#endif +{ + CONST unsigned char *bmap; + unsigned long n; + bdf_glyph_t *gp; + bdf_font_t *font; + _bdf_parse_t *p; + HBF_BBOX *fbbx; + double ps, rx, dw; + char nbuf[24]; + + /* + * Attempt to get the bitmap. + */ + if ((bmap = hbfGetBitmap(hbf, code)) == 0) + /* + * Need some sort of error handling here. + */ + return; + + p = (_bdf_parse_t *) callback_data; + + fbbx = hbfFontBBox(hbf); + + font = p->font; + + /* + * Check to make sure there is enough space to hold this glyph. If not, + * allocate 10 more just in case. + */ + if (font->glyphs_used == font->glyphs_size) { + if (font->glyphs_size == 0) + font->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * 16); + else + font->glyphs = (bdf_glyph_t *) + realloc((char *) font->glyphs, + sizeof(bdf_glyph_t) * (font->glyphs_used + 16)); + gp = font->glyphs + font->glyphs_size; + (void) memset((char *) gp, 0, sizeof(bdf_glyph_t) * 16); + font->glyphs_size += 16; + } + + gp = font->glyphs + font->glyphs_used++; + + /* + * Set the glyph name. + */ + sprintf(nbuf, "char%d", code); + n = (unsigned long) strlen(nbuf); + gp->name = (char *) malloc(n + 1); + (void) memcpy(gp->name, nbuf, n + 1); + + /* + * Set encoding. + */ + gp->encoding = (long) code; + + /* + * Set the device width. + */ + gp->dwidth = (unsigned short) fbbx->hbf_width; + + /* + * Set the scalable width. + */ + ps = (double) font->point_size; + rx = (double) font->resolution_x; + dw = (double) gp->dwidth; + gp->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx)); + + /* + * Set the glyph bounding box. + */ + gp->bbx.width = fbbx->hbf_width; + gp->bbx.height = fbbx->hbf_height; + gp->bbx.x_offset = fbbx->hbf_xDisplacement; + gp->bbx.y_offset = fbbx->hbf_yDisplacement; + gp->bbx.ascent = gp->bbx.height + gp->bbx.y_offset; + gp->bbx.descent = -gp->bbx.y_offset; + + /* + * Add the bitmap by making a copy. Assumes the font bbx is OK for + * determining the number of bytes needed for the glyph bitmap. + */ + gp->bytes = ((gp->bbx.width + 7) >> 3) * gp->bbx.height; + gp->bitmap = (unsigned char *) malloc(gp->bytes); + (void) memcpy((char *) gp->bitmap, (char *) bmap, gp->bytes); + + /* + * Call the callback if it was provided. + */ + if (p->callback != 0) { + p->cb.reason = BDF_LOADING; + p->cb.total = font->glyphs_size; + p->cb.current = font->glyphs_used; + (*p->callback)(&p->cb, p->client_data); + } +} + +bdf_font_t * +#ifdef __STDC__ +bdf_load_hbf_font(char *filename, bdf_options_t *opts, bdf_callback_t callback, + void *data) +#else +bdf_load_hbf_font(filename, opts, callback, data) +char *filename; +bdf_options_t *opts; +bdf_callback_t callback; +void *data; +#endif +{ + int n, diff; + unsigned long lineno; + FILE *in; + HBF *hbf; + bdf_property_t *pp; + char *name; + _bdf_parse_t p; + + if ((hbf = hbfOpen(filename)) == 0) + return 0; + + if ((in = fopen(hbfFileName(hbf), "r")) == 0) { + hbfClose(hbf); + return 0; + } + + /* + * Parse the HBF header for properties and other things. + */ + (void) memset((char *) &p, 0, sizeof(_bdf_parse_t)); + p.opts = (opts != 0) ? opts : &_bdf_opts; + p.minlb = 32767; + p.callback = callback; + p.client_data = data; + + n = _bdf_readlines(fileno(in), _bdf_parse_hbf_header, (void *) &p, + &lineno); + + fclose(in); + + /* + * Determine what spacing the font has so the monowidth field can be set + * if necessary. + */ + if ((pp = bdf_get_font_property(p.font, "SPACING")) != 0) { + switch (pp->value.atom[0]) { + case 'p': case 'P': p.font->spacing = BDF_PROPORTIONAL; break; + case 'm': case 'M': p.font->spacing = BDF_MONOWIDTH; break; + case 'c': case 'C': p.font->spacing = BDF_CHARCELL; break; + } + } + + /* + * Set the monowidth field if necessary. + */ + if (p.font->spacing != BDF_PROPORTIONAL) + p.font->monowidth = p.font->bbx.width; + + /* + * Before loading the glyphs, check to see if any glyph structures have + * been added. If not, check the HBF font for the number of characters. + * Dynamically increasing glyph storage causes memory fragmentation on + * some machines and crashes. This takes care of the cases where the HBF + * file does not provide a "CHARS n" line. + */ + if (p.font->glyphs_size < hbfChars(hbf)) { + if (p.font->glyphs_size == 0) + p.font->glyphs = (bdf_glyph_t *) + malloc(sizeof(bdf_glyph_t) * hbfChars(hbf)); + else + p.font->glyphs = (bdf_glyph_t *) + realloc((char *) p.font->glyphs, + sizeof(bdf_glyph_t) * hbfChars(hbf)); + diff = hbfChars(hbf) - p.font->glyphs_size; + (void) memset((char *) (p.font->glyphs + p.font->glyphs_size), 0, + diff); + p.font->glyphs_size = hbfChars(hbf); + } + + /* + * Call the callback initially to set things up. + */ + if (p.callback != 0) { + p.cb.reason = BDF_LOAD_START; + p.cb.total = p.font->glyphs_size; + p.cb.current = 0; + (*p.callback)(&p.cb, p.client_data); + } + + /* + * Now load the glyphs. + */ + hbfForEach(hbf, _bdf_add_hbf_glyph, (void *) &p); + + /* + * Close the HBF font. + */ + hbfClose(hbf); + + /* + * Sort the glyphs by encoding. + */ + qsort((char *) p.font->glyphs, p.font->glyphs_used, sizeof(bdf_glyph_t), + by_encoding); + + /* + * After loading the HBF header, create an XLFD name. If the XLFD name + * cannot be made then preserve the name found in the HBF file. + */ + if ((name = bdf_make_xlfd_name(p.font, "HBF", "Unknown")) != 0) { + if (p.font->name != 0) + /* + * If a name already exists in the font, free it up. + */ + free(p.font->name); + + /* + * Replace the old name with the XLFD name. + */ + p.font->name = name; + } + + /* + * Mark the font as being modified and generate a message that says + * something about the font being converted from HBF format. + */ + p.font->modified = 1; + _bdf_add_acmsg(p.font, "Font converted from HBF to BDF.", 31); + + return p.font; +} +#endif /* HAVE_HBF */ + +/* + * Crop the glyph bitmap to the minimum rectangle needed to hold the bits that + * are set. Adjust the metrics based on the provided bounding box. + */ +static void +#ifdef __STDC__ +_bdf_crop_glyph(bdf_font_t *font, bdf_glyph_t *glyph) +#else +_bdf_crop_glyph(font, glyph) +bdf_font_t *font; +bdf_glyph_t *glyph; +#endif +{ + int byte; + unsigned short x, y, bpr, nbpr, col, colx, si, di; + unsigned short minx, maxx, miny, maxy; + unsigned long bytes; + unsigned char *bmap, *masks; + bdf_bbx_t nbbx; + + if (glyph == 0) + return; + + (void) memcpy((char *) &nbbx, (char *) &glyph->bbx, sizeof(bdf_bbx_t)); + + bpr = ((glyph->bbx.width * font->bpp) + 7) >> 3; + + maxx = maxy = 0; + minx = miny = 32767; + + masks = 0; + switch (font->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + for (y = 0; y < glyph->bbx.height; y++) { + for (col = x = 0; x < glyph->bbx.width; x++, col += font->bpp) { + si = (col & 7) / font->bpp; + if (glyph->bitmap[(y * bpr) + (col >> 3)] & masks[si]) { + minx = MIN(minx, x); + maxx = MAX(maxx, x); + miny = MIN(miny, y); + maxy = MAX(maxy, y); + } + } + } + + /* + * Handle an empty bitmap as a special case. + */ + if (minx == 32767) { + if (glyph->bytes > 0) + free((char *) glyph->bitmap); + glyph->bytes = 0; + (void) memset((char *) &glyph->bbx, 0, sizeof(bdf_bbx_t)); + return; + } + + /* + * Increment the max points so width and height calculations won't go + * wrong. + */ + maxx++; + maxy++; + + if (minx > 0) + nbbx.x_offset += minx; + if (maxx - minx != nbbx.width) + nbbx.width = maxx - minx; + + if (miny > 0) + nbbx.ascent -= miny; + if (maxy - miny != nbbx.height) + nbbx.height = maxy - miny; + nbbx.descent = nbbx.height - nbbx.ascent; + nbbx.y_offset = -nbbx.descent; + + nbpr = ((nbbx.width * font->bpp) + 7) >> 3; + + /* + * If nothing changed, then the glyph is already contained in the + * minimum rectangle. + */ + if (memcmp((char *) &nbbx, (char *) &glyph->bbx, + sizeof(bdf_bbx_t)) == 0 || + (nbpr == bpr && nbbx.height == glyph->bbx.height)) + return; + + /* + * The metrics changed, so a new bitmap is needed. + */ + bytes = nbpr * nbbx.height; + bmap = (unsigned char *) malloc(bytes); + (void) memset((char *) bmap, 0, bytes); + + colx = minx * font->bpp; + for (y = miny; y < maxy; y++) { + for (col = x = minx; x < maxx; x++, col += font->bpp) { + si = (col & 7) / font->bpp; + byte = glyph->bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + /* + * Position the pixel in the byte if necessary. + */ + di = ((col - colx) & 7) / font->bpp; + if (di < si) + byte <<= (si - di) * font->bpp; + else if (di > si) + byte >>= (di - si) * font->bpp; + bmap[((y - miny) * nbpr) + ((col - colx) >> 3)] |= byte; + } + } + } + + if (glyph->bytes > 0) + free((char *) glyph->bitmap); + glyph->bytes = bytes; + glyph->bitmap = bmap; + + (void) memcpy((char *) &glyph->bbx, (char *) &nbbx, sizeof(bdf_bbx_t)); +} + +/* + * Pad a character-cell font glyph to match the bounds specified in the + * provided bounding box. + */ +static void +#ifdef __STDC__ +_bdf_pad_cell(bdf_font_t *font, bdf_glyph_t *glyph, bdf_glyph_t *cell) +#else +_bdf_pad_cell(font, glyph, cell) +bdf_font_t *font; +bdf_glyph_t *glyph, *cell; +#endif +{ + bdf_bbx_t *bbx; + unsigned short si, di, sx, byte; + unsigned short x, y, dx, dy, bx, by, bpr, nbpr; + unsigned char *bmap, *masks; + + masks = 0; + switch (font->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + bbx = &font->bbx; + + if (glyph->bbx.width == bbx->width && glyph->bbx.height == bbx->height) { + /* + * The glyph is already positioned in the cell. Copy the bitmap + * and return. + */ + (void) memcpy((char *) cell->bitmap, (char *) glyph->bitmap, + cell->bytes); + return; + } + + /* + * Determine the X and Y location of the baseline. + */ + bx = MYABS(bbx->x_offset - glyph->bbx.x_offset); + by = (bbx->ascent + bbx->descent) + bbx->y_offset; + + bpr = ((glyph->bbx.width * font->bpp) + 7) >> 3; + nbpr = ((bbx->width * font->bpp) + 7) >> 3; + + /* + * Set various cell values and clear the cell bitmap. + */ + bmap = cell->bitmap; + (void) memset((char *) bmap, 0, cell->bytes); + + for (dy = by - glyph->bbx.ascent, y = 0; y < glyph->bbx.height; + y++, dy++) { + for (dx = bx * font->bpp, sx = x = 0; x < glyph->bbx.width; + x++, dx += font->bpp, sx += font->bpp) { + si = (sx & 7) / font->bpp; + byte = glyph->bitmap[(y * bpr) + (sx >> 3)] & masks[si]; + if (byte) { + di = (dx & 7) / font->bpp; + if (di < si) + byte <<= (si - di) * font->bpp; + else if (di > si) + byte >>= (di - si) * font->bpp; + bmap[(dy * nbpr) + (dx >> 3)] |= byte; + } + } + } +} + +static char *unix_eol = "\n"; +static char *dos_eol = "\r\n"; +static char *mac_eol = "\r"; + +void +#ifdef __STDC__ +bdf_save_font(FILE *out, bdf_font_t *font, bdf_options_t *opts, + bdf_callback_t callback, void *data) +#else +bdf_save_font(out, font, opts, callback, data) +FILE *out; +bdf_font_t *font; +bdf_options_t *opts; +bdf_callback_t callback; +void *data; +#endif +{ + unsigned long i, j, bpr, pcnt; + double dw, ps, rx; + char *sp, *ep, *eol; + bdf_property_t *p; + bdf_glyph_t *c, *cp, cell; + bdf_callback_struct_t cb; + + if (font == 0) + return; + + eol = 0; + switch (opts->eol) { + case BDF_UNIX_EOL: eol = unix_eol; break; + case BDF_DOS_EOL: eol = dos_eol; break; + case BDF_MAC_EOL: eol = mac_eol; break; + } + + /* + * If the font is a character cell font, allocate some space for the + * bitmap. + */ + if (font->spacing == BDF_CHARCELL && opts->pad_cells != 0) { + bpr = ((font->bbx.width * font->bpp) + 7) >> 3; + cell.bytes = bpr * font->bbx.height; + cell.bitmap = (unsigned char *) malloc(cell.bytes); + } + + /* + * Emit the header. + */ + fprintf(out, "STARTFONT 2.1%s", eol); + + /* + * Emit the comments. + */ + if (font->comments_len > 0) { + for (sp = font->comments; *sp; sp++) { + ep = sp; + while (*ep && *ep != '\n') + ep++; + fprintf(out, "COMMENT %.*s%s", ep - sp, sp, eol); + sp = ep; + } + } + + /* + * Emit the font name. + */ + fprintf(out, "FONT %s%s", font->name, eol); + + /* + * Emit the size info. + */ + if (font->bpp == 1) + fprintf(out, "SIZE %ld %ld %ld%s", font->point_size, + font->resolution_x, font->resolution_y, eol); + else + fprintf(out, "SIZE %ld %ld %ld %hd%s", font->point_size, + font->resolution_x, font->resolution_y, font->bpp, eol); + + /* + * Emit the bounding box. + */ + fprintf(out, "FONTBOUNDINGBOX %hd %hd %hd %hd%s", + font->bbx.width, font->bbx.height, font->bbx.x_offset, + font->bbx.y_offset, eol); + + /* + * Emit the properties after counting how many are properties and + * how many are comments. + */ + for (i = pcnt = 0, p = font->props; i < font->props_used; i++, p++) { + if (memcmp(p->name, "COMMENT", 7) != 0) + pcnt++; + } + + fprintf(out, "STARTPROPERTIES %ld%s", pcnt, eol); + for (i = 0, p = font->props; i < font->props_used; i++, p++) { + fprintf(out, "%s ", p->name); + if (p->format == BDF_ATOM) { + if (p->value.atom == 0) + fprintf(out, "\"\"%s", eol); + else + fprintf(out, "\"%s\"%s", p->value.atom, eol); + } else + fprintf(out, "%ld%s", p->value.int32, eol); + } + + fprintf(out, "ENDPROPERTIES%s", eol); + + /* + * Emit the number of bitmaps in the font. + */ + fprintf(out, "CHARS %ld%s", font->unencoded_used + font->glyphs_used, eol); + + /* + * Call the callback if it was passed to start the save. + */ + if (callback != 0) { + cb.reason = BDF_SAVE_START; + cb.total = font->unencoded_used + font->glyphs_used; + cb.current = 0; + (*callback)(&cb, data); + } + + /* + * Emit the unencoded bitmaps. + */ + for (i = 0, cp = font->unencoded; i < font->unencoded_used; i++, cp++) { + /* + * If the font has character-cell spacing and the option to pad the + * glyphs to the size of the font bbx is set, then pad the glyph. + * Otherwise, crop the glyph to the minimum rectangle needed to hold + * the bitmap. + */ + if (font->spacing == BDF_CHARCELL && opts->pad_cells != 0) { + /* + * Point at the temporary glyph structure and copy the necessary + * glyph info into it. + */ + c = &cell; + c->name = cp->name; + c->encoding = cp->encoding; + c->swidth = cp->swidth; + c->dwidth = cp->dwidth; + (void) memcpy((char *) &c->bbx, (char *) &font->bbx, + sizeof(bdf_bbx_t)); + _bdf_pad_cell(font, cp, c); + } else { + c = cp; + _bdf_crop_glyph(font, c); + } + + /* + * If the font has monowidth or character-cell spacing, then assign + * the font monowidth field to the device width and recalculate the + * scalable width. + */ + if (font->spacing != BDF_PROPORTIONAL) { + c->dwidth = font->monowidth; + ps = (double) font->point_size; + rx = (double) font->resolution_x; + dw = (double) c->dwidth; + c->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx)); + } + if (c->name == 0) + fprintf(out, "STARTCHAR unencoded%ld%sENCODING -1%s", i, eol, eol); + else + fprintf(out, "STARTCHAR %s%sENCODING -1%s", c->name, eol, eol); + fprintf(out, "SWIDTH %hd 0%sDWIDTH %hd 0%s", + c->swidth, eol, c->dwidth, eol); + fprintf(out, "BBX %hd %hd %hd %hd%s", c->bbx.width, c->bbx.height, + c->bbx.x_offset, c->bbx.y_offset, eol); + fprintf(out, "BITMAP%s", eol); + bpr = ((c->bbx.width * font->bpp) + 7) >> 3; + for (j = 0; bpr != 0 && j < c->bytes; j++) { + if (j && j % bpr == 0) + fprintf(out, eol); + fprintf(out, "%02X", c->bitmap[j]); + } + /* + * Handle empty bitmaps like this. + */ + if (c->bbx.height > 0) + fprintf(out, eol); + fprintf(out, "ENDCHAR%s", eol); + + /* + * Call the callback if supplied. + */ + if (callback != 0) { + cb.reason = BDF_SAVING; + cb.current++; + (*callback)(&cb, data); + } + } + + /* + * Emit the other bitmaps. + */ + for (i = 0, cp = font->glyphs; i < font->glyphs_used; i++, cp++) { + /* + * If the font has character-cell spacing and the option to pad the + * glyphs to the size of the font bbx is set, then pad the glyph. + * Otherwise, crop the glyph to the minimum rectangle needed to hold + * the bitmap. + */ + if (font->spacing == BDF_CHARCELL && opts->pad_cells != 0) { + /* + * Point at the temporary glyph structure and copy the necessary + * glyph info into it. + */ + c = &cell; + c->name = cp->name; + c->encoding = cp->encoding; + c->swidth = cp->swidth; + c->dwidth = cp->dwidth; + (void) memcpy((char *) &c->bbx, (char *) &font->bbx, + sizeof(bdf_bbx_t)); + _bdf_pad_cell(font, cp, c); + } else { + c = cp; + _bdf_crop_glyph(font, c); + } + + /* + * If the font has monowidth or character-cell spacing, then assign + * the font monowidth field to the device width and recalculate the + * scalable width. + */ + if (font->spacing != BDF_PROPORTIONAL) { + c->dwidth = font->monowidth; + ps = (double) font->point_size; + rx = (double) font->resolution_x; + dw = (double) c->dwidth; + c->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx)); + } + if (c->name == 0) + fprintf(out, "STARTCHAR char%ld%sENCODING %ld%s", + c->encoding, eol, c->encoding, eol); + else + fprintf(out, "STARTCHAR %s%sENCODING %ld%s", + c->name, eol, c->encoding, eol); + fprintf(out, "SWIDTH %hd 0%sDWIDTH %hd 0%s", + c->swidth, eol, c->dwidth, eol); + fprintf(out, "BBX %hd %hd %hd %hd%s", c->bbx.width, c->bbx.height, + c->bbx.x_offset, c->bbx.y_offset, eol); + fprintf(out, "BITMAP%s", eol); + bpr = ((c->bbx.width * font->bpp) + 7) >> 3; + for (j = 0; bpr != 0 && j < c->bytes; j++) { + if (j && j % bpr == 0) + fprintf(out, eol); + fprintf(out, "%02X", c->bitmap[j]); + } + /* + * Handle empty bitmaps like this. + */ + if (c->bbx.height > 0) + fprintf(out, eol); + fprintf(out, "ENDCHAR%s", eol); + + /* + * Call the callback if supplied. + */ + if (callback != 0) { + cb.reason = BDF_SAVING; + cb.current++; + (*callback)(&cb, data); + } + } + + /* + * Emit the trailer. + */ + fprintf(out, "ENDFONT%s", eol); + + /* + * Always force a final call to the callback to make sure things + * get cleaned up. + */ + if (callback != 0) { + cb.reason = BDF_SAVING; + cb.current = cb.total; + (*callback)(&cb, data); + } + + /* + * If the font is a character cell font, clean up the temporary glyph. + */ + if (font->spacing == BDF_CHARCELL && opts->pad_cells != 0) + free((char *) cell.bitmap); +} + +/* + * Routine to write a single set of SBIT metrics. + */ +void +#ifdef __STDC__ +bdf_save_sbit_metrics(FILE *out, bdf_font_t *font, bdf_options_t *opts, + char *appname) +#else +bdf_save_sbit_metrics(out, font, opts, appname) +bdf_font_t *font; +bdf_options_t *opts; +char *appname; +#endif +{ + char *eol; + + eol = 0; + switch (opts->eol) { + case BDF_UNIX_EOL: eol = unix_eol; break; + case BDF_DOS_EOL: eol = dos_eol; break; + case BDF_MAC_EOL: eol = mac_eol; break; + } + + /* + * Throw a simple header in. + */ + if (appname) + fprintf(out, ";%s; SBIT metrics file generated by \"%s\".%s;%s%s", eol, + appname, eol, eol, eol); + + /* + * Save PPEM. + */ + fprintf(out, ";%s; Pixels Per Em.%s;%s", eol, eol, eol); + fprintf(out, "PPEM %ld%s%s", font->point_size, eol, eol); + + /* + * If the font is character cell or monowidth, set this boolean. + */ + if (font->spacing != BDF_PROPORTIONAL) { + fprintf(out, + ";%s; Font is not proportional, so use mono advance.%s;%s", + eol, eol, eol); + fprintf(out, "FORCECONSTANTMETRICS TRUE%s%s", eol, eol); + } else { + fprintf(out, + ";%s; Font is proportional, so do not use mono advance.%s;%s", + eol, eol, eol); + fprintf(out, "FORCECONSTANTMETRICS FALSE%s%s", eol, eol); + } + + /* + * Do the horizontal line metrics only. + */ + fprintf(out, ";%s; Horizontal line metrics.%s;%s", eol, eol, eol); + + fprintf(out, "H_ASCENDER %ld%sH_DESCENDER %ld%s", font->font_ascent, eol, + font->font_descent, eol); + fprintf(out, "H_WIDTHMAX %hd%s", font->bbx.width, eol); + fprintf(out, "H_MINORIGINSB %hd%sH_MINADVANCEBL %hd%s", + font->bbx.x_offset, eol, + font->bbx.width + font->bbx.x_offset, eol); + fprintf(out, "H_MAXBEFOREBL %hd%sH_MINAFTERBL %hd%s%s", + font->bbx.ascent, eol, font->bbx.y_offset, eol, eol); + + /* + * Write the default caret info. + */ + fprintf(out, ";%s; Caret slope and offset info.%s;%s", eol, eol, eol); + fprintf(out, "CARETSLOPENUMERATOR 1%sCARETSLOPEDENOMINATOR 0%s", eol, eol); + fprintf(out, "CARETOFFSET 0%s%s", eol, eol); + + /* + * Write the bitmap options. + */ + fprintf(out, ";%s; Bitmap options.%s;%s", eol, eol, eol); + fprintf(out, "DIRECTION H%sSTORAGE FAST%s%s", eol, eol, eol); + + /* + * Scaled bitmaps not implemented yet. + */ + fprintf(out, ";%s; Scaled bitmap info (Not Yet Implemented).%s;%s", + eol, eol, eol); +} + +/* + * Special routine to dump the font in the Roman Czyborra's hex format. It + * only dumps the encoded glyphs and assumes the bitmaps have the correct + * sizes. + */ +void +#ifdef __STDC__ +bdf_export_hex(FILE *out, bdf_font_t *font, bdf_callback_t callback, + void *data) +#else +bdf_export_hex(out, font, callback, data) +FILE *out; +bdf_font_t *font; +bdf_callback_t callback; +void *data; +#endif +{ + int bpr, fbpr, j, k; + unsigned long i, ng; + bdf_glyph_t *gp, cell; + bdf_callback_struct_t cb; + + if (font == 0 || out == 0) + return; + + if (font->glyphs_used == 0) + return; + + /* + * Call the callback if it was passed to start the export. + */ + if (callback != 0) { + cb.reason = BDF_EXPORT_START; + cb.total = font->glyphs_used; + cb.current = 0; + (*callback)(&cb, data); + } + + fbpr = ((font->bbx.width * font->bpp) + 7) >> 3; + bpr = (((font->bbx.width >> 1) * font->bpp) + 7) >> 3; + cell.bytes = fbpr * font->bbx.height; + cell.bitmap = (unsigned char *) malloc(cell.bytes); + + for (i = 0, ng = font->glyphs_used, gp = font->glyphs; i < ng; i++, gp++) { + _bdf_pad_cell(font, gp, &cell); + fprintf(out, "%04lX:", gp->encoding & 0xffff); + if (gp->bbx.width <= (font->bbx.width >> 1)) { + for (j = 0; j < cell.bytes; j += fbpr) { + for (k = 0; k < bpr; k++) + fprintf(out, "%02X", cell.bitmap[j + k]); + } + } else { + for (j = 0; j < cell.bytes; j++) + fprintf(out, "%02X", cell.bitmap[j]); + } + if (cell.bytes > 0) + putc('\n', out); + + /* + * Call the callback if supplied. + */ + if (callback != 0) { + cb.reason = BDF_EXPORTING; + cb.current++; + (*callback)(&cb, data); + } + } + + /* + * Clean up the cell. + */ + free((char *) cell.bitmap); + + /* + * Always call a final callback to make sure the client gets a chance to + * clean things up. + */ + if (callback != 0) { + cb.reason = BDF_EXPORTING; + cb.current = cb.total; + (*callback)(&cb, data); + } +} + +void +#ifdef __STDC__ +bdf_free_font(bdf_font_t *font) +#else +bdf_free_font(font) +bdf_font_t *font; +#endif +{ + unsigned long i; + bdf_glyph_t *glyphs; + + if (font == 0) + return; + + if (font->name != 0) + free(font->name); + + /* + * Free up the internal hash table of property names. + */ + hash_free((hashtable *) font->internal); + free((char *) font->internal); + + /* + * Free up the comment info. + */ + if (font->comments_len > 0) + free(font->comments); + + /* + * Free up the auto-correction messages. + */ + if (font->acmsgs_len > 0) + free(font->acmsgs); + + /* + * Free up the properties. + */ + for (i = 0; i < font->props_size; i++) { + if (font->props[i].format == BDF_ATOM && font->props[i].value.atom) + free(font->props[i].value.atom); + } + + if (font->props_size > 0 && font->props != 0) + free((char *) font->props); + + /* + * Free up the character info. + */ + for (i = 0, glyphs = font->glyphs; i < font->glyphs_used; i++, glyphs++) { + if (glyphs->name) + free(glyphs->name); + if (glyphs->bytes > 0 && glyphs->bitmap != 0) + free((char *) glyphs->bitmap); + } + + for (i = 0, glyphs = font->unencoded; i < font->unencoded_used; + i++, glyphs++) { + if (glyphs->name) + free(glyphs->name); + if (glyphs->bytes > 0) + free((char *) glyphs->bitmap); + } + + if (font->glyphs_size > 0) + free((char *) font->glyphs); + + if (font->unencoded_size > 0) + free((char *) font->unencoded); + + /* + * Free up the overflow storage if it was used. + */ + for (i = 0, glyphs = font->overflow.glyphs; i < font->overflow.glyphs_used; + i++, glyphs++) { + if (glyphs->name != 0) + free(glyphs->name); + if (glyphs->bytes > 0) + free((char *) glyphs->bitmap);; + } + if (font->overflow.glyphs_size > 0) + free((char *) font->overflow.glyphs); + + free((char *) font); +} + +void +#ifdef __STDC__ +bdf_create_property(char *name, int format) +#else +bdf_create_property(name, format) +char *name; +int format; +#endif +{ + unsigned long n; + bdf_property_t *p; + + /* + * First check to see if the property has + * already been added or not. If it has, then + * simply ignore it. + */ + + if (hash_lookup(name, &proptbl)) + return; + + if (nuser_props == 0) + user_props = (bdf_property_t *) malloc(sizeof(bdf_property_t)); + else + user_props = (bdf_property_t *) realloc((char *) user_props, + sizeof(bdf_property_t) * + (nuser_props + 1)); + + p = user_props + nuser_props; + (void) memset((char *) p, 0, sizeof(bdf_property_t)); + n = (unsigned long) (strlen(name) + 1); + p->name = (char *) malloc(n); + (void) memcpy(p->name, name, n); + p->format = format; + p->builtin = 0; + + n = _num_bdf_properties + nuser_props; + hash_insert(p->name, (void *) n, &proptbl); + + nuser_props++; +} + +bdf_property_t * +#ifdef __STDC__ +bdf_get_property(char *name) +#else +bdf_get_property(name) +char *name; +#endif +{ + hashnode hn; + unsigned long propid; + + if (name == 0 || *name == 0) + return 0; + + if ((hn = hash_lookup(name, &proptbl)) == 0) + return 0; + + propid = (unsigned long) hn->data; + if (propid >= _num_bdf_properties) + return user_props + (propid - _num_bdf_properties); + return _bdf_properties + propid; +} + +/* + * Routine to compare two property names. + */ +static int +#ifdef __STDC__ +by_prop_name(const void *a, const void *b) +#else +by_prop_name(a, b) +char *a, *b; +#endif +{ + bdf_property_t *p1, *p2; + + p1 = (bdf_property_t *) a; + p2 = (bdf_property_t *) b; + + return strcmp(p1->name, p2->name); +} + +unsigned long +#ifdef __STDC__ +bdf_property_list(bdf_property_t **props) +#else +bdf_property_list(props) +bdf_property_t **props; +#endif +{ + unsigned long n; + bdf_property_t *p; + + n = _num_bdf_properties + nuser_props; + if (props != 0 && n != 0) { + p = (bdf_property_t *) malloc(sizeof(bdf_property_t) * n); + (void) memcpy((char *) p, (char *) _bdf_properties, + sizeof(bdf_property_t) * _num_bdf_properties); + (void) memcpy((char *) (p + _num_bdf_properties), (char *) user_props, + sizeof(bdf_property_t) * nuser_props); + qsort((char *) p, n, sizeof(bdf_property_t), by_prop_name); + *props = p; + } + return n; +} + +int +#ifdef __STDC__ +bdf_replace_comments(bdf_font_t *font, char *comments, + unsigned long comments_len) +#else +bdf_replace_comments(font, comments, comments_len) +bdf_font_t *font; +char *comments; +unsigned long comments_len; +#endif +{ + if (font == 0 || comments_len == 0) + return 0; + + if (font->comments_len > 0) + free(font->comments); + + font->comments = (char *) malloc(comments_len + 1); + (void) memcpy(font->comments, comments, comments_len); + font->comments[comments_len] = 0; + font->comments_len = comments_len; + font->modified = 1; + return 1; +} + +unsigned long +#ifdef __STDC__ +bdf_font_property_list(bdf_font_t *font, bdf_property_t **props) +#else +bdf_font_property_list(font, props) +bdf_font_t *font; +bdf_property_t **props; +#endif +{ + bdf_property_t *p; + + if (font == 0 || font->props_used == 0) + return 0; + + if (props != 0) { + p = (bdf_property_t *) malloc(sizeof(bdf_property_t) * + font->props_used); + (void) memcpy((char *) p, (char *) font->props, + sizeof(bdf_property_t) * font->props_used); + qsort((char *) p, font->props_used, sizeof(bdf_property_t), + by_prop_name); + *props = p; + } + + return font->props_used; +} + +void +#ifdef __STDC__ +bdf_add_font_property(bdf_font_t *font, bdf_property_t *property) +#else +bdf_add_font_property(font, property) +bdf_font_t *font; +bdf_property_t *property; +#endif +{ + int len; + unsigned long propid; + hashnode hn; + bdf_property_t *p, *ip; + + if (property == 0 || property->name == 0 || property->name[0] == 0) + return; + + /* + * If the font does not have a property hash table yet, make + * sure it is allocated. + */ + if (font->internal == 0) { + font->internal = (void *) malloc(sizeof(hashtable)); + hash_init((hashtable *) font->internal); + } + + /* + * See if the property is in the general property table yet. + * If it isn't, then add it. + */ + if ((hn = hash_lookup(property->name, &proptbl)) == 0) + bdf_create_property(property->name, property->format); + else { + /* + * If the property exists and is a user defined property, make sure + * its format is updated to match the property being added. + */ + propid = (unsigned long) hn->data; + if (propid >= _num_bdf_properties) { + p = user_props + (propid - _num_bdf_properties); + if (p->format != property->format) + p->format = property->format; + } + } + + /* + * If the font already has this property, then change the existing one. + */ + hn = hash_lookup(property->name, (hashtable *) font->internal); + if (hn != 0) { + /* + * Changing an existing property value. + */ + p = font->props + ((unsigned long) hn->data); + + /* + * If the format changed, then free the atom value if the original + * format was an atom. + */ + if (p->format == BDF_ATOM && property->format != BDF_ATOM && + p->value.atom != 0) + free((char *) p->value.atom); + p->format = property->format; + + switch (p->format) { + case BDF_ATOM: + /* + * If the property value is the same, then just return. + */ + if (property->value.atom == p->value.atom || + (property->value.atom && p->value.atom && + strcmp(property->value.atom, p->value.atom) == 0)) + return; + if (property->value.atom == 0) + len = 1; + else + len = strlen(property->value.atom) + 1; + if (len > 1) { + p->value.atom = (char *) malloc(len); + (void) memcpy(p->value.atom, property->value.atom, len); + } else + p->value.atom = 0; + break; + case BDF_INTEGER: + /* + * If the property value is the same, then just return. + */ + if (p->value.int32 == property->value.int32) + return; + p->value.int32 = property->value.int32; + break; + case BDF_CARDINAL: + /* + * If the property value is the same, then just return. + */ + if (p->value.card32 == property->value.card32) + return; + p->value.card32 = property->value.card32; + break; + } + } else { + /* + * New property being added. + */ + + /* + * Get the internal table entry for a pointer to the + * name of the property. + */ + hn = hash_lookup(property->name, &proptbl); + propid = (unsigned long) hn->data; + if (propid >= _num_bdf_properties) + ip = user_props + (propid - _num_bdf_properties); + else + ip = _bdf_properties + propid; + + /* + * Add it to the property list first. + */ + if (font->props_used == font->props_size) { + if (font->props_size == 0) + font->props = (bdf_property_t *) malloc(sizeof(bdf_property_t)); + else + font->props = (bdf_property_t *) + realloc((char *) font->props, sizeof(bdf_property_t) * + (font->props_size + 1)); + font->props_size++; + } + p = font->props + font->props_used; + + p->name = ip->name; + p->format = ip->format; + p->builtin = ip->builtin; + + switch (p->format) { + case BDF_ATOM: + if (property->value.atom == 0) + len = 1; + else + len = strlen(property->value.atom) + 1; + if (len > 1) { + p->value.atom = (char *) malloc(len); + (void) memcpy(p->value.atom, property->value.atom, len); + } else + p->value.atom = 0; + break; + case BDF_INTEGER: + p->value.int32 = property->value.int32; + break; + case BDF_CARDINAL: + p->value.card32 = property->value.card32; + break; + } + + /* + * Now insert it into the internal hash table. + */ + hash_insert(p->name, (void *) font->props_used, + (hashtable *) font->internal); + font->props_used++; + } + + if (memcmp(property->name, "DEFAULT_CHAR", 12) == 0) + /* + * If the property just added is DEFAULT_CHAR, then make sure the + * default_glyph field is set. + */ + font->default_glyph = p->value.card32; + else if (memcmp(property->name, "FONT_ASCENT", 11) == 0) + /* + * If the property just added is FONT_ASCENT, then adjust the + * font_ascent field. + */ + font->font_ascent = p->value.int32; + else if (memcmp(property->name, "FONT_DESCENT", 12) == 0) + /* + * If the property just added is FONT_DESCENT, then adjust the + * font_descent field. + */ + font->font_descent = p->value.int32; + else if (memcmp(property->name, "RESOLUTION_X", 12) == 0) + /* + * If the property just added is RESOLUTION_X, then adjust the + * resolution_x field. + */ + font->resolution_x = p->value.card32; + else if (memcmp(property->name, "RESOLUTION_Y", 12) == 0) + /* + * If the property just added is RESOLUTION_Y, then adjust the + * resolution_y field. + */ + font->resolution_y = p->value.card32; + else if (memcmp(property->name, "POINT_SIZE", 10) == 0) + /* + * If the property just added is POINT_SIZE, then adjust the + * point_size field. + */ + font->point_size = p->value.int32 / 10; + else if (memcmp(property->name, "SPACING", 7) == 0) { + /* + * Make sure the font spacing is kept in synch if the property + * changes. If the spacing changes from proportional to one + * of the others, force the monowidth to be set. + */ + switch (p->value.atom[0]) { + case 'C': case 'c': + if (font->spacing == BDF_PROPORTIONAL) + font->monowidth = font->bbx.width + font->bbx.x_offset; + font->spacing = BDF_CHARCELL; + break; + case 'M': case 'm': + if (font->spacing == BDF_PROPORTIONAL) + font->monowidth = font->bbx.width + font->bbx.x_offset; + font->spacing = BDF_MONOWIDTH; + break; + case 'P': case 'p': font->spacing = BDF_PROPORTIONAL; break; + } + } + + /* + * Make sure the font is marked as modified. + */ + font->modified = 1; +} + +void +#ifdef __STDC__ +bdf_delete_font_property(bdf_font_t *font, char *name) +#else +bdf_delete_font_property(font, name) +bdf_font_t *font; +char *name; +#endif +{ + hashnode hn; + unsigned long off; + bdf_property_t *p; + + if (font == 0 || name == 0 || *name == 0 || font->props_used == 0) + return; + + if ((hn = hash_lookup(name, (hashtable *) font->internal)) == 0) + return; + + off = (unsigned long) hn->data; + p = font->props + off; + + /* + * Delete the ATOM value if appropriate. + */ + if (p->format == BDF_ATOM && p->value.atom != 0) + free(p->value.atom); + + /* + * The property exists. Two things needs to be done: + * 1. Remove the property from the hash table. + * 2. Remove the property from the font's list of properties. + */ + hash_delete(name, (hashtable *) font->internal); + + /* + * Locate its offset in the font property list. + */ + if (off < font->props_used - 1) + /* + * We have to shift the property list down. + */ + _bdf_memmove((char *) p, (char *) (p + 1), + sizeof(bdf_property_t) * ((font->props_used - 1) - off)); + font->props_used--; + + /* + * If the font property happens to be DEFAULT_CHAR, then make sure the + * default_glyph field is reset. + */ + if (strncmp(name, "DEFAULT_CHAR", 12) == 0) + font->default_glyph = -1; + + /* + * Update the hash table with the correct indexes. + */ + for (off = 0, p = font->props; off < font->props_used; off++, p++) + hash_insert(p->name, (void *) off, (hashtable *) font->internal); + + /* + * Mark the font as being modified. + */ + font->modified = 1; +} + +bdf_property_t * +#ifdef __STDC__ +bdf_get_font_property(bdf_font_t *font, const char *name) +#else +bdf_get_font_property(font, name) +bdf_font_t *font; +const char *name; +#endif +{ + hashnode hn; + + if (font == 0 || font->props_size == 0 || name == 0 || *name == 0) + return 0; + + hn = hash_lookup((char*)name, (hashtable *) font->internal); + return (hn) ? (font->props + ((unsigned long) hn->data)) : 0; +} + +typedef struct { + bdf_options_t *opts; + bdf_options_callback_t callback; + void *client_data; + _bdf_list_t list; +} _bdf_opts_parse_t; + +static int +#ifdef __STDC__ +_bdf_get_boolean(char *val) +#else +_bdf_get_boolean(val) +char *val; +#endif +{ + int ok; + + ok = 0; + if (val == 0 || *val == 0) + return ok; + + switch (val[0]) { + case '0': case 'F': case 'f': case 'N': case 'n': ok = 0; break; + case '1': case 'T': case 't': case 'Y': case 'y': ok = 1; break; + } + return ok; +} + +static int +#ifdef __STDC__ +_bdf_parse_options(char *line, unsigned long linelen, unsigned long lineno, + void *call_data, void *client_data) +#else +_bdf_parse_options(line, linelen, lineno, call_data, client_data) +char *line; +unsigned long linelen, lineno; +void *call_data, *client_data; +#endif +{ + _bdf_list_t *lp; + _bdf_opts_parse_t *p; + long bpp; + + p = (_bdf_opts_parse_t *) client_data; + lp = &p->list; + + /* + * Split the line into fields. + */ + _bdf_split(" \t+", line, linelen, lp); + + if (lp->field[0][0] == 'b' && + memcmp(lp->field[0], "bits_per_pixel", 14) == 0) { + if (lp->used < 2) { + fprintf(stderr, + "bdf: warning: %ld: incorrect number of fields %ld.\n", + lineno, lp->used); + fprintf(stderr, + "bdf: warning: %ld: bits_per_pixel <1, 2, or 4>.\n", + lineno); + } else { + bpp = _bdf_atol(lp->field[1], 0, 10); + if (!(bpp == 1 || bpp == 2 || bpp == 4)) { + fprintf(stderr, + "bdf: warning: %ld: invalid bits per pixel %ld.\n", + lineno, bpp); + fprintf(stderr, + "bdf: warning: %ld: bits_per_pixel <1, 2, or 4>.\n", + lineno); + } else + p->opts->bits_per_pixel = bpp; + } + return 0; + } + + if (lp->field[0][0] == 'e' && memcmp(lp->field[0], "eol", 3) == 0) { + if (lp->used < 2) { + fprintf(stderr, + "bdf: warning: %ld: incorrect number of fields %ld.\n", + lineno, lp->used); + fprintf(stderr, + "bdf: warning: %ld: eol .\n", lineno); + } else { + switch (lp->field[1][0]) { + case 'u': case 'U': p->opts->eol = BDF_UNIX_EOL; break; + case 'd': case 'D': p->opts->eol = BDF_DOS_EOL; break; + case 'm': case 'M': p->opts->eol = BDF_MAC_EOL; break; + } + } + return 0; + } + + if (lp->field[0][0] == 'c' && + memcmp(lp->field[0], "correct_metrics", 15) == 0) { + if (lp->used < 2) { + fprintf(stderr, + "bdf: warning: %ld: incorrect number of fields %ld.\n", + lineno, lp->used); + fprintf(stderr, + "bdf: warning: %ld: correct_metrics .\n", lineno); + } else + p->opts->correct_metrics = _bdf_get_boolean(lp->field[1]); + + return 0; + } + + if (lp->field[0][0] == 'k' && + memcmp(lp->field[0], "keep_unencoded", 14) == 0) { + if (lp->used < 2) { + fprintf(stderr, + "bdf: warning: %ld: incorrect number of fields %ld.\n", + lineno, lp->used); + fprintf(stderr, + "bdf: warning: %ld: keep_unencoded .\n", lineno); + } else + p->opts->keep_unencoded = _bdf_get_boolean(lp->field[1]); + + return 0; + } + + if (lp->field[0][0] == 'k' && + memcmp(lp->field[0], "keep_comments", 13) == 0) { + if (lp->used < 2) { + fprintf(stderr, + "bdf: warning: %ld: incorrect number of fields %ld.\n", + lineno, lp->used); + fprintf(stderr, + "bdf: warning: %ld: keep_comments .\n", lineno); + } else + p->opts->keep_comments = _bdf_get_boolean(lp->field[1]); + + return 0; + } + + if (lp->field[0][0] == 'p' && + memcmp(lp->field[0], "pad_character_cells", 19) == 0) { + if (lp->used < 2) { + fprintf(stderr, + "bdf: warning: %ld: incorrect number of fields %ld.\n", + lineno, lp->used); + fprintf(stderr, + "bdf: warning: %ld: pad_character_cells .\n", + lineno); + } else + p->opts->pad_cells = _bdf_get_boolean(lp->field[1]); + + return 0; + } + + if (lp->field[0][0] == 'p' && + memcmp(lp->field[0], "point_size", 10) == 0) { + if (lp->used < 2) { + fprintf(stderr, + "bdf: warning: %ld: incorrect number of fields %ld.\n", + lineno, lp->used); + fprintf(stderr, + "bdf: warning: %ld: point_size .\n", lineno); + } else + p->opts->point_size = _bdf_atol(lp->field[1], 0, 10); + return 0; + } + + if (lp->field[0][0] == 'h' && + memcmp(lp->field[0], "horizontal_resolution", 21) == 0) { + if (lp->used < 2) { + fprintf(stderr, + "bdf: warning: %ld: incorrect number of fields %ld.\n", + lineno, lp->used); + fprintf(stderr, + "bdf: warning: %ld: horizontal_resolution .\n", + lineno); + } else + p->opts->resolution_x = _bdf_atoul(lp->field[1], 0, 10); + return 0; + } + + if (lp->field[0][0] == 'v' && + memcmp(lp->field[0], "vertical_resolution", 19) == 0) { + if (lp->used < 2) { + fprintf(stderr, + "bdf: warning: %ld: incorrect number of fields %ld.\n", + lineno, lp->used); + fprintf(stderr, + "bdf: warning: %ld: vertical_resolution .\n", + lineno); + } else + p->opts->resolution_y = _bdf_atoul(lp->field[1], 0, 10); + return 0; + } + + if (lp->field[0][0] == 'f' && + memcmp(lp->field[0], "font_spacing", 12) == 0) { + if (lp->used < 2) { + fprintf(stderr, + "bdf: warning: %ld: incorrect number of fields %ld.\n", + lineno, lp->used); + fprintf(stderr, + "bdf: warning: %ld: font_spacing .\n", + lineno); + } else { + switch (lp->field[1][0]) { + case 'P': case 'p': + p->opts->font_spacing = BDF_PROPORTIONAL; + break; + case 'M': case 'm': + p->opts->font_spacing = BDF_MONOWIDTH; + break; + case 'C': case 'c': + p->opts->font_spacing = BDF_CHARCELL; + break; + default: + fprintf(stderr, + "bdf: warning: %ld: unknown font spacing '%s'.\n", + lineno, lp->field[1]); + } + } + return 0; + } + + if (lp->field[0][0] == 'p' && + memcmp(lp->field[0], "property", 8) == 0) { + if (lp->used < 3) { + fprintf(stderr, + "bdf: warning: %ld: incorrect number of fields %ld.\n", + lineno, lp->used); + fprintf(stderr, + "bdf: warning: %ld: property .\n", + lineno); + } else { + switch (lp->field[2][0]) { + case 'A': case 'a': + bdf_create_property(lp->field[1], BDF_ATOM); + break; + case 'C': case 'c': + bdf_create_property(lp->field[1], BDF_CARDINAL); + break; + case 'I': case 'i': + bdf_create_property(lp->field[1], BDF_INTEGER); + break; + default: + fprintf(stderr, + "bdf: warning: %ld: unknown property type '%s'.\n", + lineno, lp->field[2]); + } + } + return 0; + } + + if (lp->field[0][0] == 'h' && + memcmp(lp->field[0], "hint_truetype_glyphs", 20) == 0) { + if (lp->used < 2) { + fprintf(stderr, + "bdf: warning: %ld: incorrect number of fields %ld.\n", + lineno, lp->used); + fprintf(stderr, + "bdf: warning: %ld: hint_truetype_glyphs .\n", + lineno); + } else + p->opts->ttf_hint = _bdf_get_boolean(lp->field[1]); + + return 0; + } + + if (lp->field[0][0] == 'g' && + memcmp(lp->field[0], "generate_ranges", 15) == 0) + /* + * Simply ignore the glyph ranges entry in the config file. + */ + return 0; + + /* + * If the callback returns a non-zero value, the caller has handled the + * unknown option found in the file. + */ + if (p->callback != 0 && + (*p->callback)(p->opts, lp->field, lp->used, p->client_data) != 0) + return 0; + + fprintf(stderr, "bdf: warning: %ld: unknown configuration option '%s'.\n", + lineno, lp->field[0]); + return 0; +} + +void +#ifdef __STDC__ +bdf_load_options(FILE *in, bdf_options_t *opts, + bdf_options_callback_t callback, void *client_data) +#else +bdf_load_options(in, opts, callback, client_data) +FILE *in; +bdf_options_t *opts; +bdf_options_callback_t callback; +void *client_data; +#endif +{ + unsigned long lineno; + _bdf_opts_parse_t p; + + /* + * Don't bother loading the options if the file or options structure + * is NULL. + */ + if (in == 0 || opts == 0) + return; + + (void *) memset((char *) &p, 0, sizeof(_bdf_opts_parse_t)); + p.opts = opts; + p.callback = callback; + p.client_data = client_data; + (void) _bdf_readlines(fileno(in), _bdf_parse_options, (void *) &p, + &lineno); + + /* + * Free up the list if there is any space allocated. + */ + if (p.list.size > 0) + free((char *) p.list.field); +} + +void +#ifdef __STDC__ +bdf_save_options(FILE *out, bdf_options_t *opts) +#else +bdf_save_options(out, opts) +FILE *out; +bdf_options_t *opts; +#endif +{ + unsigned long i; + + if (out == 0 || opts == 0) + return; + + fprintf(out, "#\n# Metrics corrections.\n#\ncorrect_metrics "); + if (opts->correct_metrics) + fprintf(out, "true\n\n"); + else + fprintf(out, "false\n\n"); + + fprintf(out, "#\n# Preserve unencoded glyphs.\n#\nkeep_unencoded "); + if (opts->keep_unencoded) + fprintf(out, "true\n\n"); + else + fprintf(out, "false\n\n"); + + fprintf(out, "#\n# Preserve comments.\n#\nkeep_comments "); + if (opts->keep_comments) + fprintf(out, "true\n\n"); + else + fprintf(out, "false\n\n"); + + fprintf(out, "#\n# Pad character cells.\n#\npad_character_cells "); + if (opts->pad_cells) + fprintf(out, "true\n\n"); + else + fprintf(out, "false\n\n"); + + fprintf(out, "#\n# Font spacing.\n#\nfont_spacing "); + switch (opts->font_spacing) { + case BDF_PROPORTIONAL: fprintf(out, "proportional\n\n"); break; + case BDF_MONOWIDTH: fprintf(out, "monowidth\n\n"); break; + case BDF_CHARCELL: fprintf(out, "charactercell\n\n"); break; + } + + fprintf(out, "#\n# Point size.\n#\npoint_size %ld\n\n", opts->point_size); + + fprintf(out, + "#\n# Horizontal resolution.\n#\nhorizontal_resolution %ld\n\n", + opts->resolution_x); + + fprintf(out, + "#\n# Vertical resolution.\n#\nvertical_resolution %ld\n\n", + opts->resolution_x); + + fprintf(out, + "#\n# Bits per pixel.\n#\nbits_per_pixel %d\n\n", + opts->bits_per_pixel); + + fprintf(out, "#\n# Hint TrueType glyphs.\n#\nhint_truetype_glyphs "); + if (opts->ttf_hint) + fprintf(out, "true\n\n"); + else + fprintf(out, "false\n\n"); + + fprintf(out, "#\n# Set the EOL used when writing BDF fonts.\n#\neol "); + switch (opts->eol) { + case BDF_UNIX_EOL: fprintf(out, "unix\n\n"); break; + case BDF_DOS_EOL: fprintf(out, "dos\n\n"); break; + case BDF_MAC_EOL: fprintf(out, "mac\n\n"); break; + } + + /* + * Write out the user defined properties if they exist. + */ + if (nuser_props == 0) + return; + + fprintf(out, "#\n# User defined properties.\n#\n"); + + for (i = 0; i < nuser_props; i++) { + fprintf(out, "property %s ", user_props[i].name); + switch (user_props[i].format) { + case BDF_ATOM: fprintf(out, "atom\n"); break; + case BDF_CARDINAL: fprintf(out, "cardinal\n"); break; + case BDF_INTEGER: fprintf(out, "integer\n"); break; + } + } +} + +void +#ifdef __STDC__ +bdf_default_options(bdf_options_t *opts) +#else +bdf_default_options(opts) +bdf_options_t *opts; +#endif +{ + if (opts == 0) + return; + + (void) memcpy((char *) opts, (char *) &_bdf_opts, sizeof(bdf_options_t)); +} + +bdf_font_t * +#ifdef __STDC__ +bdf_new_font(char *name, long point_size, long resolution_x, long resolution_y, + long spacing, int bpp) +#else +bdf_new_font(name, point_size, resolution_x, resolution_y, spacing, bpp) +char *name; +long point_size, resolution_x, resolution_y, spacing; +int bpp; +#endif +{ + long psize; + char sp[2]; + bdf_font_t *font; + double dp, dr; + bdf_property_t prop; + + font = (bdf_font_t *) calloc(1, sizeof(bdf_font_t)); + if (name != 0 && *name != 0) { + font->name = (char *) malloc(strlen(name) + 1); + (void) strcpy(font->name, name); + } + + font->bpp = bpp; + font->point_size = point_size; + font->resolution_x = resolution_x; + font->resolution_y = resolution_y; + + /* + * Determine the pixel size of the new font based on the + * point size and resolution. + */ + dr = (double) resolution_y; + dp = (double) (point_size * 10); + psize = (long) (((dp * dr) / 722.7) + 0.5); + + /* + * Make the default width about 1.5 smaller than the height. + */ + font->bbx.height = psize; + font->bbx.width = (unsigned short) ((double) psize) / 1.5; + + /* + * Now determine the default ascent and descent assuming a + * the descent is about 1/4 the ascent. + */ + font->bbx.descent = psize >> 2; + font->bbx.ascent = psize - font->bbx.descent; + + font->bbx.y_offset = -font->bbx.descent; + + /* + * Allocation the internal hash tables. + */ + font->internal = (void *) malloc(sizeof(hashtable)); + hash_init((hashtable *) font->internal); + + font->default_glyph = -1; + font->spacing = spacing; + + /* + * Add various useful properties. + */ + prop.name = "POINT_SIZE"; + prop.format = BDF_INTEGER; + prop.value.int32 = font->point_size * 10; + bdf_add_font_property(font, &prop); + + prop.name = "PIXEL_SIZE"; + prop.format = BDF_INTEGER; + prop.value.int32 = psize; + bdf_add_font_property(font, &prop); + + prop.name = "RESOLUTION_X"; + prop.format = BDF_CARDINAL; + prop.value.card32 = (unsigned long) font->resolution_x; + bdf_add_font_property(font, &prop); + + prop.name = "RESOLUTION_Y"; + prop.format = BDF_CARDINAL; + prop.value.card32 = (unsigned long) font->resolution_y; + bdf_add_font_property(font, &prop); + + prop.name = "FONT_ASCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = (long) font->bbx.ascent; + bdf_add_font_property(font, &prop); + + prop.name = "FONT_DESCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = (long) font->bbx.descent; + bdf_add_font_property(font, &prop); + + prop.name = "AVERAGE_WIDTH"; + prop.format = BDF_INTEGER; + prop.value.int32 = font->bbx.width * 10; + bdf_add_font_property(font, &prop); + + sp[0] = 'P'; + sp[1] = 0; + switch (spacing) { + case BDF_PROPORTIONAL: sp[0] = 'P'; break; + case BDF_MONOWIDTH: sp[0] = 'M'; break; + case BDF_CHARCELL: sp[0] = 'C'; break; + } + prop.name = "SPACING"; + prop.format = BDF_ATOM; + prop.value.atom = sp; + bdf_add_font_property(font, &prop); + + /* + * Mark the font as unmodified. + */ + font->modified = 0; + + return font; +} + +void +#ifdef __STDC__ +bdf_set_default_metrics(bdf_font_t *font) +#else +bdf_set_default_metrics(font) +bdf_font_t *font; +#endif +{ + long psize; + double dp, dr; + bdf_property_t prop; + + /* + * Determine the pixel size of the new font based on the + * point size and resolution. + */ + dr = (double) font->resolution_y; + dp = (double) (font->point_size * 10); + psize = (long) (((dp * dr) / 722.7) + 0.5); + + /* + * Make the default width about 1.5 smaller than the height. + */ + font->bbx.height = psize; + font->bbx.width = (unsigned short) ((double) psize) / 1.5; + + /* + * Now determine the default ascent and descent assuming a + * the descent is about 1/4 the ascent. + */ + font->bbx.descent = psize >> 2; + font->bbx.ascent = psize - font->bbx.descent; + + font->bbx.y_offset = -font->bbx.descent; + + font->default_glyph = -1; + + /* + * Add various useful properties. + */ + prop.name = "FONT_ASCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = (long) font->bbx.ascent; + bdf_add_font_property(font, &prop); + + prop.name = "FONT_DESCENT"; + prop.format = BDF_INTEGER; + prop.value.int32 = (long) font->bbx.descent; + bdf_add_font_property(font, &prop); + + prop.name = "AVERAGE_WIDTH"; + prop.format = BDF_INTEGER; + prop.value.int32 = font->bbx.width * 10; + bdf_add_font_property(font, &prop); +} + +int +#ifdef __STDC__ +bdf_glyph_modified(bdf_font_t *font, long which, int unencoded) +#else +bdf_glyph_modified(font, which, unencoded) +bdf_font_t *font; +long which; +int unencoded; +#endif +{ + if (font == 0 || which < 0) + return 0; + + if (unencoded) + return _bdf_glyph_modified(font->umod, which); + else + return _bdf_glyph_modified(font->nmod, which); +} + +void +#ifdef __STDC__ +bdf_copy_glyphs(bdf_font_t *font, long start, long end, + bdf_glyphlist_t *glyphs, int unencoded) +#else +bdf_copy_glyphs(font, start, end, glyphs, unencoded) +bdf_font_t *font; +long start, end; +bdf_glyphlist_t *glyphs; +int unencoded; +#endif +{ + long tmp, i, nc; + bdf_glyph_t *cp, *dp; + short maxas, maxds, maxrb, minlb, maxlb, rb; + + if (start > end) { + tmp = end; + end = start; + start = tmp; + } + + glyphs->bpp = font->bpp; + glyphs->start = start; + glyphs->end = end; + glyphs->glyphs_used = 0; + + tmp = (end - start) + 1; + if (tmp > glyphs->glyphs_size) { + if (glyphs->glyphs_size == 0) + glyphs->glyphs = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * tmp); + else + glyphs->glyphs = (bdf_glyph_t *) realloc((char *) glyphs->glyphs, + sizeof(bdf_glyph_t) * tmp); + cp = glyphs->glyphs + glyphs->glyphs_size; + (void) memset((char *) cp, 0, + sizeof(bdf_glyph_t) * (tmp - glyphs->glyphs_size)); + glyphs->glyphs_size = tmp; + } + + /* + * Clear out bitmaps and names in the existing entries. + */ + for (cp = glyphs->glyphs, i = 0; i < glyphs->glyphs_size; i++, cp++) { + if (cp->name != 0) + free(cp->name); + if (cp->bytes > 0) + free((char *) cp->bitmap); + } + + /* + * Zero out everything. + */ + (void) memset((char *) &glyphs->bbx, 0, sizeof(bdf_bbx_t)); + (void) memset((char *) glyphs->glyphs, 0, + sizeof(bdf_glyph_t) * glyphs->glyphs_size); + + /* + * Initialize the bounds used to generate the overall bounding box for the + * set of glyphs being copied. + */ + minlb = font->bbx.width; + maxlb = maxrb = maxas = maxds = 0; + + /* + * Do the copy. + */ + nc = (unencoded == 0) ? font->glyphs_used : font->unencoded_used; + cp = (unencoded == 0) ? font->glyphs : font->unencoded; + dp = glyphs->glyphs; + + for (i = 0; + i < nc && ((unencoded && i <= end) || cp->encoding <= end); + i++, cp++) { + if ((unencoded && i >= start) || cp->encoding >= start) { + (void) memcpy((char *) dp, (char *) cp, sizeof(bdf_glyph_t)); + if (cp->name != 0) { + dp->name = (char *) malloc(strlen(cp->name) + 1); + (void) strcpy(dp->name, cp->name); + } + if (cp->bytes > 0) { + dp->bytes = cp->bytes; + dp->bitmap = (unsigned char *) malloc(cp->bytes); + (void) memcpy((char *) dp->bitmap, (char *) cp->bitmap, + cp->bytes); + } + + /* + * Determine the overall metrics for the group of characters being + * copied. + */ + maxas = MAX(cp->bbx.ascent, maxas); + maxds = MAX(cp->bbx.descent, maxds); + rb = cp->bbx.width + cp->bbx.x_offset; + maxrb = MAX(rb, maxrb); + minlb = MIN(cp->bbx.x_offset, minlb); + maxlb = MAX(cp->bbx.x_offset, maxlb); + + glyphs->glyphs_used++; + dp++; + } + } + + /* + * Set the overall metrics for this set of glyphs. + */ + glyphs->bbx.width = maxrb - minlb; + glyphs->bbx.x_offset = minlb; + + glyphs->bbx.height = maxas + maxds; + glyphs->bbx.ascent = maxas; + glyphs->bbx.descent = maxds; + glyphs->bbx.y_offset = -maxds; +} + +void +#ifdef __STDC__ +bdf_delete_glyphs(bdf_font_t *font, long start, long end, int unencoded) +#else +bdf_delete_glyphs(font, start, end, unencoded) +bdf_font_t *font; +long start, end; +int unencoded; +#endif +{ + long i, n, nc, cnt; + bdf_glyph_t *cp, *sp, *ep; + + if (font == 0) + return; + + if (start > end) { + cnt = end; + end = start; + start = cnt; + } + + nc = (unencoded == 0) ? font->glyphs_used : font->unencoded_used; + cp = (unencoded == 0) ? font->glyphs : font->unencoded; + sp = ep = 0; + + for (i = 0; i < nc && cp->encoding <= end; i++, cp++) { + if (cp->encoding >= start && sp == 0) + sp = cp; + } + ep = cp; + if (sp == 0) + sp = ep; + + if (ep > sp) { + /* + * There are some glyphs to delete. + * 1. Free the name and bitmap fields of the glyphs being deleted. + * 2. Move the end range down if necessary. + * 3. Clear the glyphs on the end if a move was done. + */ + + /* + * Mark the font as being modified. + */ + font->modified = 1; + + cnt = ep - sp; + + for (cp = sp; cp < ep; cp++) { + /* + * Mark the glyphs being deleted as also being modified so the + * empty cells can be shown correctly by the client programs. + */ + if (unencoded) + _bdf_set_glyph_modified(font->umod, cp->encoding); + else + _bdf_set_glyph_modified(font->nmod, cp->encoding); + + if (cp->name != 0) + free(cp->name); + if (cp->bytes > 0) + free((char *) cp->bitmap); + } + + cp = (unencoded == 0) ? font->glyphs : font->unencoded; + + /* + * Check to see if there are some glyphs that need to + * be moved down. + */ + if (ep - cp < nc) { + /* + * Shift the glyphs down. + */ + n = nc - (ep - cp); + _bdf_memmove((char *) sp, (char *) ep, sizeof(bdf_glyph_t) * n); + + /* + * Set the starting point for the clear. + */ + ep = sp + n; + } else + /* + * Set the starting point for the clear. + */ + ep = sp; + + /* + * Clear the glyph space just moved. + */ + n = nc - (ep - cp); + (void) memset((char *) ep, 0, sizeof(bdf_glyph_t) * n); + + /* + * Adjust the number of glyphs used. + */ + if (unencoded == 0) + font->glyphs_used -= cnt; + else + font->unencoded_used -= cnt; + + /* + * If unencoded glyphs were deleted, re-encode all + * of them to cause a shift when everything is redrawn. + */ + if (unencoded != 0) { + for (i = 0, cp = font->unencoded; i < font->unencoded_used; + i++, cp++) { + if (_bdf_glyph_modified(font->umod, cp->encoding)) { + _bdf_clear_glyph_modified(font->umod, cp->encoding); + _bdf_set_glyph_modified(font->umod, i); + } + cp->encoding = i; + } + } + } +} + +/* + * Routines for quick and dirty dithering. + */ +static void +#ifdef __STDC__ +_bdf_one_to_n(bdf_glyphlist_t *gl, int n) +#else +_bdf_one_to_n(gl, n) +bdf_glyphlist_t *gl; +int n; +#endif +{ + long i; + unsigned short bpr, sbpr, bytes, col, sx, sy; + unsigned char *nbmap, *masks; + bdf_glyph_t *gp; + + if (gl == 0 || gl->glyphs_used == 0) + return; + + masks = 0; + switch (n) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + gl->bpp = n; + for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) { + if (gp->bbx.width == 0 || gp->bbx.height == 0) + continue; + sbpr = (gp->bbx.width + 7) >> 3; + bpr = ((gp->bbx.width * n) + 7) >> 3; + bytes = bpr * gp->bbx.height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < gp->bbx.height; sy++) { + for (col = sx = 0; sx < gp->bbx.width; sx++, col += n) { + if (gp->bitmap[(sy * sbpr) + (sx >> 3)] & (0x80 >> (sx & 7))) + nbmap[(sy * bpr) + (col >> 3)] |= masks[(col & 7) / n]; + } + } + free((char *) gp->bitmap); + gp->bytes = bytes; + gp->bitmap = nbmap; + } +} + +static void +#ifdef __STDC__ +_bdf_n_to_one(bdf_glyphlist_t *gl) +#else +_bdf_n_to_one(gl) +bdf_glyphlist_t *gl; +#endif +{ + long i; + unsigned short bpr, sbpr, bytes, col, sx, sy; + unsigned char *nbmap, *masks; + bdf_glyph_t *gp; + + if (gl == 0 || gl->glyphs_used == 0) + return; + + masks = 0; + switch (gl->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) { + if (gp->bbx.width == 0 || gp->bbx.height == 0) + continue; + sbpr = ((gp->bbx.width * gl->bpp) + 7) >> 3; + bpr = (gp->bbx.width + 7) >> 3; + bytes = bpr * gp->bbx.height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < gp->bbx.height; sy++) { + for (col = sx = 0; sx < gp->bbx.width; sx++, col += gl->bpp) { + if (gp->bitmap[(sy * sbpr) + (col >> 3)] & + masks[(col & 7) / gl->bpp]) + nbmap[(sy * bpr) + (sx >> 3)] |= (0x80 >> (sx & 7)); + } + } + free((char *) gp->bitmap); + gp->bytes = bytes; + gp->bitmap = nbmap; + } + gl->bpp = 1; +} + +static void +#ifdef __STDC__ +_bdf_two_to_four(bdf_glyphlist_t *gl) +#else +_bdf_two_to_four(gl) +bdf_glyphlist_t *gl; +#endif +{ + long i; + unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy; + unsigned char *nbmap, *masks; + bdf_glyph_t *gp; + + if (gl == 0 || gl->glyphs_used == 0) + return; + + masks = twobpp; + + for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) { + if (gp->bbx.width == 0 || gp->bbx.height == 0) + continue; + sbpr = ((gp->bbx.width << 1) + 7) >> 3; + bpr = ((gp->bbx.width << 2) + 7) >> 3; + bytes = bpr * gp->bbx.height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < gp->bbx.height; sy++) { + for (col = sx = 0; sx < gp->bbx.width; sx++, col += 2) { + si = (col & 7) >> 1; + byte = gp->bitmap[(sy * sbpr) + (col >> 3)] & masks[si]; + if (byte) { + /* + * Shift the byte down to make an index. + */ + if (si < 3) + byte >>= (3 - si) * gl->bpp; + + /* + * Scale the index to four bits per pixel and shift it into + * place before adding it. + */ + byte = (byte << 2) + 3; + if ((sx & 1) == 0) + byte <<= 4; + nbmap[(sy * bpr) + ((sx << 2) >> 3)] |= byte; + } + } + } + free((char *) gp->bitmap); + gp->bytes = bytes; + gp->bitmap = nbmap; + } + gl->bpp = 4; +} + +static void +#ifdef __STDC__ +_bdf_four_to_two(bdf_glyphlist_t *gl) +#else +_bdf_four_to_two(gl) +bdf_glyphlist_t *gl; +#endif +{ + long i; + unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy; + unsigned char *nbmap, *masks; + bdf_glyph_t *gp; + + if (gl == 0 || gl->glyphs_used == 0) + return; + + masks = fourbpp; + + gl->bpp = 2; + for (gp = gl->glyphs, i = 0; i < gl->glyphs_used; i++, gp++) { + sbpr = ((gp->bbx.width << 2) + 7) >> 3; + bpr = ((gp->bbx.width << 1) + 7) >> 3; + bytes = bpr * gp->bbx.height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < gp->bbx.height; sy++) { + for (col = sx = 0; sx < gp->bbx.width; sx++, col += 4) { + si = (col & 7) >> 2; + byte = gp->bitmap[(sy * sbpr) + (col >> 3)] & masks[si]; + if (byte) { + /* + * Shift the byte down to make an index. + */ + if (si == 0) + byte >>= 4; + + /* + * Scale the index to two bits per pixel and shift it into + * place if necessary. + */ + byte >>= 2; + + si = ((sx << 1) & 7) >> 1; + if (si < 3) + byte <<= (3 - si) << 1; + + nbmap[(sy * bpr) + ((sx << 1) >> 3)] |= byte; + } + } + } + free((char *) gp->bitmap); + gp->bytes = bytes; + gp->bitmap = nbmap; + } +} + +int +#ifdef __STDC__ +bdf_replace_glyphs(bdf_font_t *font, long start, bdf_glyphlist_t *glyphs, + int unencoded) +#else +bdf_replace_glyphs(font, start, glyphs, unencoded) +bdf_font_t *font; +long start; +bdf_glyphlist_t *glyphs; +int unencoded; +#endif +{ + int resize, appending; + long i, n, ng, end, del, remaining, off[2]; + bdf_glyph_t *sgp, *gp, *dgp; + short maxas, maxds, maxrb, minlb, maxlb, rb; + double ps, rx, dw; + bdf_bbx_t nbbx; + + resize = 0; + + if (font == 0) + return resize; + + /* + * Dither the incoming bitmaps so they match the same bits per pixel as + * the font. + */ + if (glyphs->bpp != font->bpp) { + if (glyphs->bpp == 1) + _bdf_one_to_n(glyphs, font->bpp); + else if (font->bpp == 1) + _bdf_n_to_one(glyphs); + else if (glyphs->bpp == 2) + _bdf_two_to_four(glyphs); + else + _bdf_four_to_two(glyphs); + } + + /* + * Set the point size and horizontal resolution so the scalable width can + * be determined. + */ + ps = (double) font->point_size; + rx = (double) font->resolution_x; + + /* + * Determine if a resize is needed. + */ + + /* + * Determine the bounding box for the font without the characters being + * replaced. + */ + minlb = 32767; + maxlb = maxrb = maxas = maxds = 0; + + /* + * Get the font bounds. + */ + maxas = MAX(font->bbx.ascent, maxas); + maxds = MAX(font->bbx.descent, maxds); + rb = font->bbx.width + font->bbx.x_offset; + maxrb = MAX(rb, maxrb); + minlb = MIN(font->bbx.x_offset, minlb); + maxlb = MAX(font->bbx.x_offset, maxlb); + + /* + * Get the bounds of the incoming glyphs. + */ + maxas = MAX(glyphs->bbx.ascent, maxas); + maxds = MAX(glyphs->bbx.descent, maxds); + rb = glyphs->bbx.width + glyphs->bbx.x_offset; + maxrb = MAX(rb, maxrb); + minlb = MIN(glyphs->bbx.x_offset, minlb); + maxlb = MAX(glyphs->bbx.x_offset, maxlb); + + /* + * Set up the new font bounding box, minus the characters that are being + * removed and with the new characters added. + */ + nbbx.width = maxrb - minlb; + nbbx.x_offset = minlb; + + nbbx.height = maxas + maxds; + nbbx.ascent = maxas; + nbbx.descent = maxds; + nbbx.y_offset = -maxds; + + /* + * Now determine if the combination of the glyphs removed and the new + * glyphs cause the font bounding box to be changed. + */ + resize = (nbbx.width > font->bbx.width || + nbbx.height > font->bbx.height) ? 1 : 0; + + /* + * Set the pointers to the glyphs. + */ + ng = (unencoded == 0) ? font->glyphs_used : font->unencoded_used; + sgp = gp = (unencoded == 0) ? font->glyphs : font->unencoded; + + /* + * Locate the closest glyph on or following `start'. + */ + for (i = 0; i < ng && gp->encoding < start; i++, gp++) ; + + appending = (i == ng); + + /* + * Set the starting point for copying the incoming glyphs. + */ + dgp = gp; + + n = glyphs->end - glyphs->start; + end = start + n; + + /* + * Delete all the glyphs between `start' and `end'. + */ + for (del = 0, i = start; i <= end; i++) { + /* + * Mark the character as being modified. + */ + if (ng > 0 && !appending && gp->encoding == i) { + if (unencoded == 0) + _bdf_set_glyph_modified(font->nmod, i); + else + _bdf_set_glyph_modified(font->umod, i); + + if (gp->name != 0) + free(gp->name); + if (gp->bytes > 0) + free((char *) gp->bitmap); + del++; + gp++; + } + } + + /* + * Determine how many glyphs remain following the last one deleted. + */ + remaining = ng - (gp - sgp); + + if (glyphs->glyphs_used == 0) { + /* + * If the glyph list is empty, then shift any remaining glyphs down + * to the destination. + */ + _bdf_memmove((char *) dgp, (char *) gp, + sizeof(bdf_glyph_t) * remaining); + if (unencoded == 0) + font->glyphs_used -= del; + else + font->unencoded_used -= del; + } else { + /* + * Insert the glyph list after making sure there is enough space to + * hold them. Also adjust the encoding and scalable width values + * after copying the glyphs. + */ + if (unencoded == 0) { + n = (font->glyphs_used - del) + glyphs->glyphs_used; + if (n > font->glyphs_size) { + off[0] = gp - sgp; + off[1] = dgp - sgp; + if (font->glyphs_size == 0) + font->glyphs = sgp = + (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * n); + else + font->glyphs = sgp = + (bdf_glyph_t *) realloc((char *) font->glyphs, + sizeof(bdf_glyph_t) * n); + gp = sgp + off[0]; + dgp = sgp + off[1]; + font->glyphs_size = n; + } + + /* + * Calculate how many will need to be shifted. + */ + if ((n = glyphs->glyphs_used - del) >= font->glyphs_used) + n = 0; + } else { + n = (font->unencoded_used - del) + glyphs->glyphs_used; + if (n > font->unencoded_size) { + off[0] = gp - sgp; + off[1] = dgp - sgp; + if (font->unencoded_size == 0) + font->unencoded = sgp = + (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * n); + else + font->unencoded = sgp = + (bdf_glyph_t *) realloc((char *) font->unencoded, + sizeof(bdf_glyph_t) * n); + gp = sgp + off[0]; + dgp = sgp + off[1]; + font->unencoded_size = n; + } + + /* + * Calculate how many will need to be shifted. + */ + if ((n = glyphs->glyphs_used - del) >= font->unencoded_used) + n = 0; + } + + /* + * Shift any following glyphs up or down if needed. + */ + if (n) + _bdf_memmove((char *) (gp + n), (char *) gp, + sizeof(bdf_glyph_t) * remaining); + + /* + * Copy the incoming glyphs, copy their names and bitmaps, + * set their encodings, and set their scalable widths. + */ + (void) memcpy((char *) dgp, (char *) glyphs->glyphs, + sizeof(bdf_glyph_t) * glyphs->glyphs_used); + for (i = 0; i < glyphs->glyphs_used; i++, dgp++) { + if (dgp->name != 0) + dgp->name = (char *) _bdf_strdup((unsigned char *) dgp->name, + strlen(dgp->name) + 1); + + if (dgp->bytes > 0) + dgp->bitmap = _bdf_strdup(dgp->bitmap, dgp->bytes); + + dgp->encoding = start + (dgp->encoding - glyphs->start); + + /* + * Mark the glyph as being modified in case it fills a cell that + * was empty before. + */ + _bdf_set_glyph_modified(font->nmod, dgp->encoding); + + dw = (double) dgp->dwidth; + dgp->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx)); + } + + /* + * Adjust the count of glyphs. + */ + ng = (ng - del) + glyphs->glyphs_used; + if (unencoded == 0) + font->glyphs_used = ng; + else + font->unencoded_used = ng; + } + + /* + * Last, if the replacement was done in the unencoded section, + * reencode all the glyphs so they show up properly. + */ + if (unencoded != 0) { + for (i = 0; i < ng; i++, sgp++) { + if (_bdf_glyph_modified(font->umod, sgp->encoding)) { + _bdf_clear_glyph_modified(font->umod, sgp->encoding); + _bdf_set_glyph_modified(font->umod, i); + } + sgp->encoding = i; + } + } + + if (resize) + (void) memcpy((char *) &font->bbx, (char *) &nbbx, sizeof(bdf_bbx_t)); + + font->modified = 1; + + return resize; +} + +int +#ifdef __STDC__ +bdf_insert_glyphs(bdf_font_t *font, long start, bdf_glyphlist_t *glyphs) +#else +bdf_insert_glyphs(font, start, glyphs) +bdf_font_t *font; +long start; +bdf_glyphlist_t *glyphs; +#endif +{ + int resize; + unsigned long i, ng, n, which; + bdf_glyph_t *gp; + + resize = 0; + + if (font == 0) + return resize; + + /* + * Dither the incoming bitmaps so they match the same bits per pixel as + * the font. + */ + if (glyphs->bpp != font->bpp) { + if (glyphs->bpp == 1) + _bdf_one_to_n(glyphs, font->bpp); + else if (font->bpp == 1) + _bdf_n_to_one(glyphs); + else if (glyphs->bpp == 2) + _bdf_two_to_four(glyphs); + else + _bdf_four_to_two(glyphs); + } + + /* + * Locate the starting glyph. + */ + gp = font->glyphs; + ng = font->glyphs_used; + for (i = 0; i < ng && gp->encoding < start; i++, gp++) ; + + /* + * If there are no glyphs at the starting point, then simply do a replace. + */ + if (i == ng) + return bdf_replace_glyphs(font, start, glyphs, 0); + + /* + * Go through the glyphs that would be shifted due to the insertion and + * determine if some of them will overflow the 0xffff boundary. + */ + n = (glyphs->end - glyphs->start) + 1; + for (which = i; i < ng; i++, gp++) { + if (gp->encoding + n > 0xffff) + break; + } + + if (i < ng) { + /* + * Some glyphs have to be moved to the unencoded area because they + * would overflow the 0xffff boundary if they were moved up. + */ + bdf_copy_glyphs(font, gp->encoding, font->glyphs[ng - 1].encoding, + &font->overflow, 0); + bdf_delete_glyphs(font, gp->encoding, font->glyphs[ng - 1].encoding, + 0); + resize += bdf_replace_glyphs(font, font->unencoded_used, + &font->overflow, 1); + } + + /* + * Go back to the insertion point and shift the remaining glyph encodings + * up by `n'. + */ + for (gp = font->glyphs + which; which < font->glyphs_used; which++, gp++) { + /* + * Mark the new glyph locations as being modified. + */ + gp->encoding += n; + _bdf_set_glyph_modified(font->nmod, gp->encoding); + } + + /* + * Finally, mark the font as being modified and insert the new glyphs. + */ + font->modified = 1; + + return resize + bdf_replace_glyphs(font, start, glyphs, 0); +} + +static void +#ifdef __STDC__ +_bdf_combine_glyphs(bdf_font_t *font, bdf_glyph_t *f, bdf_glyph_t *g) +#else +_bdf_combine_glyphs(font, f, g) +bdf_font_t *font; +bdf_glyph_t *f, *g; +#endif +{ + unsigned short x, sx, sy, si, dx, dy, di, byte, dbpr, fbpr, gbpr; + short maxas, maxds, maxrb, minlb, maxlb, rb; + unsigned char *masks; + bdf_bbx_t nbbx; + bdf_glyph_t tmp; + + /* + * Determine the max bounding box for the two glyphs. + */ + minlb = 32767; + maxlb = maxrb = maxas = maxds = 0; + + /* + * Get the font glyph bounds. + */ + maxas = MAX(f->bbx.ascent, maxas); + maxds = MAX(f->bbx.descent, maxds); + rb = f->bbx.width + f->bbx.x_offset; + maxrb = MAX(rb, maxrb); + minlb = MIN(f->bbx.x_offset, minlb); + maxlb = MAX(f->bbx.x_offset, maxlb); + + /* + * Get the bounds of the incoming glyph. + */ + maxas = MAX(g->bbx.ascent, maxas); + maxds = MAX(g->bbx.descent, maxds); + rb = g->bbx.width + g->bbx.x_offset; + maxrb = MAX(rb, maxrb); + minlb = MIN(g->bbx.x_offset, minlb); + maxlb = MAX(g->bbx.x_offset, maxlb); + + /* + * Set up the new glyph bounding box. + */ + nbbx.width = maxrb - minlb; + nbbx.x_offset = minlb; + nbbx.height = maxas + maxds; + nbbx.ascent = maxas; + nbbx.descent = maxds; + nbbx.y_offset = -maxds; + + masks = 0; + switch (font->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + fbpr = ((f->bbx.width * font->bpp) + 7) >> 3; + gbpr = ((g->bbx.width * font->bpp) + 7) >> 3; + dbpr = ((nbbx.width * font->bpp) + 7) >> 3; + + if (memcmp((char *) &nbbx, (char *) &f->bbx, sizeof(bdf_bbx_t)) == 0) { + /* + * The largest is the first, so merge the second in with it. + */ + dy = f->bbx.ascent - g->bbx.ascent; + for (sy = 0; sy < g->bbx.height; sy++, dy++) { + for (x = sx = 0; x < g->bbx.width; x++, sx += font->bpp) { + si = (sx & 7) / font->bpp; + if ((byte = g->bitmap[(sy * gbpr) + (sx >> 3)] & masks[si])) + /* + * No shifting of the byte is needed because the x offset + * is the same for both glyphs. + */ + f->bitmap[(dy * fbpr) + (sx >> 3)] |= byte; + } + } + } else if (memcmp((char *) &nbbx, (char *) &g->bbx, + sizeof(bdf_bbx_t)) == 0) { + /* + * The largest is the incoming glyph, so merge into that one and swap + * it with the font glyph. + */ + dy = g->bbx.ascent - f->bbx.ascent; + for (sy = 0; sy < f->bbx.height; sy++, dy++) { + for (x = sx = 0; x < f->bbx.width; x++, sx += font->bpp) { + si = (sx & 7) / font->bpp; + if ((byte = f->bitmap[(sy * gbpr) + (sx >> 3)] & masks[si])) + /* + * No shifting of the byte is needed because the x offset + * is the same for both glyphs. + */ + g->bitmap[(dy * fbpr) + (sx >> 3)] |= byte; + } + } + + /* + * Now swap the two glyphs while preserving the name and encoding of + * the first glyph. + */ + tmp.swidth = g->swidth; + tmp.dwidth = g->dwidth; + tmp.bytes = g->bytes; + tmp.bitmap = g->bitmap; + (void) memcpy((char *) &tmp.bbx, (char *) &g->bbx, sizeof(bdf_bbx_t)); + + g->swidth = f->swidth; + g->dwidth = f->dwidth; + g->bytes = f->bytes; + g->bitmap = f->bitmap; + (void) memcpy((char *) &g->bbx, (char *) &f->bbx, sizeof(bdf_bbx_t)); + + f->swidth = tmp.swidth; + f->dwidth = tmp.dwidth; + f->bytes = tmp.bytes; + f->bitmap = tmp.bitmap; + (void) memcpy((char *) &f->bbx, (char *) &tmp.bbx, sizeof(bdf_bbx_t)); + } else { + /* + * Need a new bitmap for the combination of the two. + */ + tmp.bytes = nbbx.height * dbpr; + tmp.bitmap = (unsigned char *) malloc(tmp.bytes); + (void) memset((char *) tmp.bitmap, 0, tmp.bytes); + + /* + * Merge the first glyph. + */ + dy = nbbx.ascent - f->bbx.ascent; + for (sy = 0; sy < f->bbx.height; sy++, dy++) { + dx = MYABS(nbbx.x_offset - f->bbx.x_offset) * font->bpp; + for (x = sx = 0; x < f->bbx.width; x++, + sx += font->bpp, dx += font->bpp) { + si = (sx & 7) / font->bpp; + if ((byte = f->bitmap[(sy * fbpr) + (sx >> 3)] & masks[si])) { + di = (dx & 7) / font->bpp; + if (di < si) + byte <<= (si - di) * font->bpp; + else if (di > si) + byte >>= (di - si) * font->bpp; + tmp.bitmap[(dy * dbpr) + (dx >> 3)] |= byte; + } + } + } + + /* + * Merge the second glyph. + */ + dy = nbbx.ascent - g->bbx.ascent; + for (sy = 0; sy < g->bbx.height; sy++, dy++) { + dx = MYABS(nbbx.x_offset - g->bbx.x_offset) * font->bpp; + for (x = sx = 0; x < g->bbx.width; x++, + sx += font->bpp, dx += font->bpp) { + si = (sx & 7) / font->bpp; + if ((byte = g->bitmap[(sy * gbpr) + (sx >> 3)] & masks[si])) { + di = (dx & 7) / font->bpp; + if (di < si) + byte <<= (si - di) * font->bpp; + else if (di > si) + byte >>= (di - si) * font->bpp; + tmp.bitmap[(dy * dbpr) + (dx >> 3)] |= byte; + } + } + } + + /* + * Now clear the font glyph and copy the temp glyph to it. + */ + if (f->bytes > 0) + free((char *) f->bitmap); + f->bytes = tmp.bytes; + f->bitmap = tmp.bitmap; + (void) memcpy((char *) &f->bbx, (char *) &nbbx, sizeof(bdf_bbx_t)); + + /* + * Set the device width. Pay attention to whether the font is + * monowidth or character cell. + */ + if (font->spacing != BDF_PROPORTIONAL) + f->dwidth = font->monowidth; + else + f->dwidth = MAX(f->dwidth, g->dwidth); + } +} + +int +#ifdef __STDC__ +bdf_merge_glyphs(bdf_font_t *font, long start, bdf_glyphlist_t *glyphs, + int unencoded) +#else +bdf_merge_glyphs(font, start, glyphs, unencoded) +bdf_font_t *font; +long start; +bdf_glyphlist_t *glyphs; +int unencoded; +#endif +{ + int resize; + long i, n, ng, end, add, enc, off; + bdf_glyph_t *sgp, *gp, *dgp, *base; + short maxas, maxds, maxrb, minlb, maxlb, rb; + double ps, rx, dw; + bdf_bbx_t nbbx; + + resize = 0; + + if (font == 0) + return resize; + + /* + * If the glyphs are being merged in the unencoded area, simply append + * them. The unencoded area is simply storage. + */ + if (unencoded) + return bdf_replace_glyphs(font, font->unencoded_used, glyphs, unencoded); + + /* + * Dither the incoming bitmaps so they match the same bits per pixel as + * the font. + */ + if (glyphs->bpp != font->bpp) { + if (glyphs->bpp == 1) + _bdf_one_to_n(glyphs, font->bpp); + else if (font->bpp == 1) + _bdf_n_to_one(glyphs); + else if (glyphs->bpp == 2) + _bdf_two_to_four(glyphs); + else + _bdf_four_to_two(glyphs); + } + + /* + * Set the point size and horizontal resolution so the scalable width can + * be determined. + */ + ps = (double) font->point_size; + rx = (double) font->resolution_x; + + /* + * Determine if a resize is needed. + */ + + /* + * Determine the bounding box for the font without the characters being + * replaced. + */ + minlb = 32767; + maxlb = maxrb = maxas = maxds = 0; + + /* + * Get the font bounds. + */ + maxas = MAX(font->bbx.ascent, maxas); + maxds = MAX(font->bbx.descent, maxds); + rb = font->bbx.width + font->bbx.x_offset; + maxrb = MAX(rb, maxrb); + minlb = MIN(font->bbx.x_offset, minlb); + maxlb = MAX(font->bbx.x_offset, maxlb); + + /* + * Get the bounds of the incoming glyphs. + */ + maxas = MAX(glyphs->bbx.ascent, maxas); + maxds = MAX(glyphs->bbx.descent, maxds); + rb = glyphs->bbx.width + glyphs->bbx.x_offset; + maxrb = MAX(rb, maxrb); + minlb = MIN(glyphs->bbx.x_offset, minlb); + maxlb = MAX(glyphs->bbx.x_offset, maxlb); + + /* + * Set up the new font bounding box, minus the characters that are being + * removed and with the new characters added. + */ + nbbx.width = maxrb - minlb; + nbbx.x_offset = minlb; + + nbbx.height = maxas + maxds; + nbbx.ascent = maxas; + nbbx.descent = maxds; + nbbx.y_offset = -maxds; + + /* + * Now determine if the combination of the glyphs removed and the new + * glyphs cause the font bounding box to be changed. + */ + resize = (nbbx.width > font->bbx.width || + nbbx.height > font->bbx.height) ? 1 : 0; + + /* + * Set the pointers to the glyphs. + */ + ng = (unencoded == 0) ? font->glyphs_used : font->unencoded_used; + gp = (unencoded == 0) ? font->glyphs : font->unencoded; + + /* + * Locate the closest glyph on or following `start'. + */ + for (i = 0; i < ng && gp->encoding < start; i++, gp++) ; + + if (i == ng) + /* + * If the gylphs are being added off the end of the list, simply insert + * them so any overflows can be handled. + */ + return bdf_insert_glyphs(font, start, glyphs); + + /* + * Set the starting point for copying the incoming glyphs. + */ + dgp = gp; + + n = glyphs->end - glyphs->start; + end = start + n; + + /* + * Count the number of glyphs that will be added and mark all the + * glyphs that will be modified. + */ + for (sgp = glyphs->glyphs, add = 0, i = start; i <= end; i++) { + enc = (sgp->encoding - glyphs->start) + start; + + /* + * Mark the glyph as being modified. + */ + if (unencoded == 0) + _bdf_set_glyph_modified(font->nmod, enc); + else + _bdf_set_glyph_modified(font->umod, enc); + + if (enc == gp->encoding) + sgp++; + else if (enc < gp->encoding) { + add++; + sgp++; + } + + if (gp->encoding == i) + gp++; + } + + if (add > 0) { + ng += add; + + /* + * Need to make room for some glyphs that will be added. + */ + if (unencoded) { + off = dgp - font->unencoded; + if (font->unencoded_used == 0) + font->unencoded = + (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * ng); + else + font->unencoded = + (bdf_glyph_t *) realloc((char *) font->unencoded, + sizeof(bdf_glyph_t) * ng); + dgp = font->unencoded + off; + font->unencoded_used = ng; + } else { + off = dgp - font->glyphs; + if (font->glyphs_used == 0) + font->glyphs = + (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t) * ng); + else + font->glyphs = + (bdf_glyph_t *) realloc((char *) font->glyphs, + sizeof(bdf_glyph_t) * ng); + dgp = font->glyphs + off; + font->glyphs_used = ng; + } + } + + /* + * Now go through and do two things: + * 1. Insert new incoming glyphs. + * 2. Combine two glyphs at the same location. + */ + base = (!unencoded) ? font->glyphs : font->unencoded; + for (gp = dgp, sgp = glyphs->glyphs, i = start; i <= end; i++) { + enc = (sgp->encoding - glyphs->start) + start; + if (enc < gp->encoding) { + /* + * Shift the glyphs up by one and add this one. + */ + if (gp - base < ng) + _bdf_memmove((char *) (gp + 1), (char *) gp, + sizeof(bdf_glyph_t) * (ng - (gp - base))); + (void) memcpy((char *) gp, (char *) sgp, sizeof(bdf_glyph_t)); + gp->name = (char *) _bdf_strdup((unsigned char *) gp->name, + strlen(gp->name) + 1); + if (gp->bytes > 0) + gp->bitmap = _bdf_strdup(gp->bitmap, gp->bytes); + gp->encoding = i; + sgp++; + dw = (double) gp->dwidth; + gp->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx)); + } else if (enc == gp->encoding) { + _bdf_combine_glyphs(font, gp, sgp); + dw = (double) gp->dwidth; + gp->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx)); + sgp++; + } + if (gp->encoding == i) + gp++; + } + + if (resize) + (void) memcpy((char *) &font->bbx, (char *) &nbbx, sizeof(bdf_bbx_t)); + + font->modified = 1; + + return resize; +} + +void +#ifdef __STDC__ +bdf_set_modified(bdf_font_t *font, int modified) +#else +bdf_set_modified(font, modified) +bdf_font_t *font; +int modified; +#endif +{ + if (font == 0 || font->modified == modified) + return; + + if (modified == 0) { + /* + * Clear out the modified bitmaps. + */ + (void) memset((char *) font->nmod, 0, sizeof(unsigned long) * 2048); + (void) memset((char *) font->umod, 0, sizeof(unsigned long) * 2048); + } + font->modified = modified; +} + +/************************************************************************** + * + * XLFD font name functions. + * + **************************************************************************/ + +static char *xlfdfields[] = { + "FOUNDRY", + "FAMILY_NAME", + "WEIGHT_NAME", + "SLANT", + "SETWIDTH_NAME", + "ADD_STYLE_NAME", + "PIXEL_SIZE", + "POINT_SIZE", + "RESOLUTION_X", + "RESOLUTION_Y", + "SPACING", + "AVERAGE_WIDTH", + "CHARSET_REGISTRY", + "CHARSET_ENCODING", +}; + +int +#ifdef __STDC__ +bdf_has_xlfd_name(bdf_font_t *font) +#else +bdf_has_xlfd_name(font) +bdf_font_t *font; +#endif +{ + unsigned long len; + char name[256]; + _bdf_list_t list; + + if (font == 0 || font->name == 0 || font->name[0] == 0) + return 0; + + len = (unsigned long) (strlen(font->name) + 1); + (void) memcpy(name, font->name, len); + list.size = list.used = 0; + _bdf_split("-", name, len, &list); + if (list.size > 0) + free((char *) list.field); + + return (list.used == 15); +} + +char * +#ifdef __STDC__ +bdf_make_xlfd_name(bdf_font_t *font, char *foundry, char *family) +#else +bdf_make_xlfd_name(font, foundry, family) +bdf_font_t *font; +char *foundry, *family; +#endif +{ + int len; + double dp, dr; + unsigned long i, width, used; + unsigned short awidth, pxsize; + bdf_property_t *pp; + bdf_glyph_t *gp; + char spacing, nbuf[256], *np, *name; + const char *val; + + if (font == 0 || bdf_has_xlfd_name(font)) + return 0; + + np = nbuf; + + /* + * Add the FOUNDRY field. + */ + if ((pp = bdf_get_font_property(font, "FOUNDRY")) != 0) + foundry = pp->value.atom; + sprintf(np, "-%s", foundry); + np += strlen(np); + + /* + * Add the FAMILY_NAME field. + */ + if ((pp = bdf_get_font_property(font, "FAMILY_NAME")) != 0) + family = pp->value.atom; + sprintf(np, "-%s", family); + np += strlen(np); + + /* + * Add the WEIGHT_NAME field. + */ + val = ((pp = bdf_get_font_property(font, "WEIGHT_NAME")) != 0) ? + pp->value.atom : "Medium"; + if (val == 0) + val = "Medium"; + sprintf(np, "-%s", val); + np += strlen(np); + + /* + * Add the SLANT field. + */ + val = ((pp = bdf_get_font_property(font, "SLANT")) != 0) ? + pp->value.atom : "R"; + if (val == 0) + val = "R"; + sprintf(np, "-%s", val); + np += strlen(np); + + /* + * Add the SETWIDTH_NAME field. + */ + val = ((pp = bdf_get_font_property(font, "SETWIDTH_NAME")) != 0) ? + pp->value.atom : "Normal"; + if (val == 0) + val = "Normal"; + sprintf(np, "-%s", val); + np += strlen(np); + + /* + * Add the ADD_STYLE_NAME field. + */ + val = ((pp = bdf_get_font_property(font, "ADD_STYLE_NAME")) != 0) ? + pp->value.atom : ""; + if (val == 0) + val = ""; + sprintf(np, "-%s", val); + np += strlen(np); + + /* + * Add the PIXEL_SIZE field. + */ + if ((pp = bdf_get_font_property(font, "PIXEL_SIZE")) != 0) + sprintf(np, "-%ld", pp->value.int32); + else { + /* + * Determine the pixel size. + */ + dp = (double) (font->point_size * 10); + dr = (double) font->resolution_y; + pxsize = (unsigned short) (((dp * dr) / 722.7) + 0.5); + sprintf(np, "-%hd", pxsize); + } + np += strlen(np); + + /* + * Add the POINT_SIZE field. + */ + if ((pp = bdf_get_font_property(font, "POINT_SIZE")) != 0) + sprintf(np, "-%ld", pp->value.int32); + else + sprintf(np, "-%ld", font->point_size * 10); + np += strlen(np); + + /* + * Add the RESOLUTION_X field. + */ + if ((pp = bdf_get_font_property(font, "RESOLUTION_X")) != 0) + sprintf(np, "-%ld", pp->value.card32); + else + sprintf(np, "-%ld", font->resolution_x); + np += strlen(np); + + /* + * Add the RESOLUTION_Y field. + */ + if ((pp = bdf_get_font_property(font, "RESOLUTION_Y")) != 0) + sprintf(np, "-%ld", pp->value.card32); + else + sprintf(np, "-%ld", font->resolution_y); + np += strlen(np); + + /* + * Add the SPACING field. + */ + if ((pp = bdf_get_font_property(font, "SPACING")) != 0) + spacing = pp->value.atom[0]; + else { + spacing = 'P'; + switch (font->spacing) { + case BDF_PROPORTIONAL: spacing = 'P'; break; + case BDF_MONOWIDTH: spacing = 'M'; break; + case BDF_CHARCELL: spacing = 'C'; break; + } + } + sprintf(np, "-%c", spacing); + np += strlen(np); + + /* + * Add the AVERAGE_WIDTH field. + */ + if ((pp = bdf_get_font_property(font, "AVERAGE_WIDTH")) != 0) + sprintf(np, "-%ld", pp->value.int32); + else { + /* + * Determine the average width of all the glyphs in the font. + */ + width = 0; + for (i = 0, gp = font->unencoded; i < font->unencoded_used; i++, gp++) + width += gp->dwidth; + for (i = 0, gp = font->glyphs; i < font->glyphs_used; i++, gp++) + width += gp->dwidth; + + used = font->unencoded_used + font->glyphs_used; + if (used == 0) + awidth = font->bbx.width * 10; + else + awidth = (unsigned short) ((((float) width) / + ((float) used)) * 10.0); + sprintf(np, "-%hd", awidth); + } + np += strlen(np); + + /* + * Add the CHARSET_REGISTRY field. + */ + val = ((pp = bdf_get_font_property(font, "CHARSET_REGISTRY")) != 0) ? + pp->value.atom : "FontSpecific"; + sprintf(np, "-%s", val); + np += strlen(np); + + /* + * Add the CHARSET_ENCODING field. + */ + val = ((pp = bdf_get_font_property(font, "CHARSET_ENCODING")) != 0) ? + pp->value.atom : "0"; + sprintf(np, "-%s", val); + np += strlen(np); + + len = (np - nbuf) + 1; + name = (char *) malloc(len); + (void) memcpy(name, nbuf, len); + return name; +} + +void +#ifdef __STDC__ +bdf_update_name_from_properties(bdf_font_t *font) +#else +bdf_update_name_from_properties(font) +bdf_font_t *font; +#endif +{ + unsigned long i; + bdf_property_t *p; + _bdf_list_t list; + char *np, name[128], nname[128]; + + if (font == 0 || bdf_has_xlfd_name(font) == 0) + return; + + (void) memset((char *) &list, 0, sizeof(_bdf_list_t)); + + /* + * Split the name into fields and shift out the first empty field. + * This assumes that the font has a name. + */ + i = (unsigned long) strlen(font->name); + (void) memcpy(name, font->name, i + 1); + _bdf_split("-", name, i, &list); + _bdf_shift(1, &list); + + /* + * Initialize the pointer to the new name and add the '-' prefix. + */ + np = nname; + *np++ = '-'; + *np = 0; + + for (i = 0; i < 14; i++) { + if ((p = bdf_get_font_property(font, xlfdfields[i])) != 0) { + /* + * The property exists, so add it to the new font name. + */ + switch (p->format) { + case BDF_ATOM: + if (p->value.atom != 0) + sprintf(np, "%s", p->value.atom); + break; + case BDF_CARDINAL: + sprintf(np, "%ld", p->value.card32); + break; + case BDF_INTEGER: + sprintf(np, "%ld", p->value.int32); + break; + } + } else + /* + * The property does not exist, so add the original value to the + * new font name. + */ + sprintf(np, "%s", list.field[i]); + np += strlen(np); + if (i + 1 < 14) { + *np++ = '-'; + *np = 0; + } + } + + /* + * Replace the existing font name with the new one. + */ + free(font->name); + i = (unsigned long) (strlen(nname) + 1); + font->name = (char *) malloc(i); + (void) memcpy(font->name, nname, i); + + /* + * Free up the list. + */ + if (list.size > 0) + free((char *) list.field); + + font->modified = 1; +} + +void +#ifdef __STDC__ +bdf_update_properties_from_name(bdf_font_t *font) +#else +bdf_update_properties_from_name(font) +bdf_font_t *font; +#endif +{ + unsigned long i; + bdf_property_t *p, prop; + _bdf_list_t list; + char name[128]; + + if (font == 0 || font->name == 0 || bdf_has_xlfd_name(font) == 0) + return; + + (void) memset((char *) &list, 0, sizeof(_bdf_list_t)); + + /* + * Split the name into fields and shift out the first empty field. + */ + i = (unsigned long) strlen(font->name); + (void) memcpy(name, font->name, i + 1); + _bdf_split("-", name, i, &list); + _bdf_shift(1, &list); + + for (i = 0; i < 14; i++) { + p = bdf_get_property(xlfdfields[i]); + prop.name = p->name; + prop.format = p->format; + switch (prop.format) { + case BDF_ATOM: + prop.value.atom = list.field[i]; + break; + case BDF_CARDINAL: + prop.value.card32 = _bdf_atoul(list.field[i], 0, 10); + break; + case BDF_INTEGER: + prop.value.int32 = _bdf_atol(list.field[i], 0, 10); + break; + } + bdf_add_font_property(font, &prop); + } + + /* + * Free up the list. + */ + if (list.size > 0) + free((char *) list.field); + + font->modified = 1; +} + +int +#ifdef __STDC__ +bdf_update_average_width(bdf_font_t *font) +#else +bdf_update_average_width(font) +bdf_font_t *font; +#endif +{ + int changed; + unsigned long i; + long oaw, awidth, used; + bdf_glyph_t *gp; + _bdf_list_t list; + bdf_property_t *pp, prop; + char *np, num[16], nbuf[128]; + + changed = 0; + + used = font->unencoded_used + font->glyphs_used; + if (used == 0) + awidth = font->bbx.width * 10; + else { + for (i = 0, awidth = 0, gp = font->unencoded; i < font->unencoded_used; + i++, gp++) + awidth += gp->dwidth; + for (i = 0, gp = font->glyphs; i < font->glyphs_used; i++, gp++) + awidth += gp->dwidth; + awidth = (long) ((((double) awidth) / ((double) used)) * 10.0); + } + + /* + * Check to see if it is different than the average width in the font + * name. + */ + if (bdf_has_xlfd_name(font)) { + (void) memset((char *) &list, 0, sizeof(_bdf_list_t)); + i = (unsigned long) strlen(font->name); + (void) memcpy(nbuf, font->name, i + 1); + _bdf_split("-", nbuf, i, &list); + oaw = _bdf_atol(list.field[12], 0, 10); + if (oaw != awidth) { + /* + * Construct a new font name with the new average width. + */ + changed = 1; + sprintf(num, "%ld", awidth); + used = strlen(num) - strlen(list.field[12]); + if (used > 0) { + /* + * Resize the string used for the font name instead of + * creating a new one. + */ + used += i; + font->name = (char *) realloc(font->name, used); + } + + /* + * Copy the elements of the list back into the new font name. + */ + np = font->name; + *np++ = '-'; + for (i = 1; i < list.used; i++) { + if (i == 12) + strcpy(np, num); + else + strcpy(np, list.field[i]); + np += strlen(np); + if (i + 1 < list.used) + *np++ = '-'; + } + } + + /* + * Clear up any space allocated for the list. + */ + if (list.size > 0) + free((char *) list.field); + } + + /* + * Now check for the AVERAGE_WIDTH property. + */ + if ((pp = bdf_get_font_property(font, "AVERAGE_WIDTH")) != 0) { + if (pp->value.int32 != awidth) { + changed = 1; + pp->value.int32 = awidth; + } + } else { + /* + * Property doesn't exist yet, so add it. + */ + changed = 1; + prop.name = "AVERAGE_WIDTH"; + prop.format = BDF_INTEGER; + prop.value.int32 = awidth; + bdf_add_font_property(font, &prop); + } + + if (changed) + font->modified = 1; + + return changed; +} + +/* + * Change the font bounding box and return a non-zero number if this causes + * the font to get larger or smaller. + */ +int +#ifdef __STDC__ +bdf_set_font_bbx(bdf_font_t *font, bdf_metrics_t *metrics) +#else +bdf_set_font_bbx(font, metrics) +bdf_font_t *font; +bdf_metrics_t *metrics; +#endif +{ + int resize; + + resize = 0; + + if (font == 0 || metrics == 0) + return resize; + + resize = (font->bbx.width != metrics->width || + font->bbx.height != metrics->height) ? 1 : 0; + + font->bbx.width = metrics->width; + font->bbx.height = metrics->height; + font->bbx.x_offset = metrics->x_offset; + font->bbx.y_offset = metrics->y_offset; + font->bbx.ascent = metrics->ascent; + font->bbx.descent = metrics->descent; + + /* + * If the font is not proportional, then make sure the monowidth field is + * set to the font bounding box. + */ + if (font->spacing != BDF_PROPORTIONAL) + font->monowidth = font->bbx.width; + + return resize; +} + +static bdf_glyph_t * +#ifdef __STDC__ +_bdf_locate_glyph(bdf_font_t *font, long code, int unencoded) +#else +_bdf_locate_glyph(font, code, unencoded) +bdf_font_t *font; +long code; +int unencoded; +#endif +{ + long l, r, m, nc; + bdf_glyph_t *gl; + + if (code < 0 || font == 0) + return 0; + + if ((unencoded && font->unencoded_used == 0) || + font->glyphs_used == 0) + return 0; + + if (unencoded) { + gl = font->unencoded; + nc = font->unencoded_used; + } else { + gl = font->glyphs; + nc = font->glyphs_used; + } + for (l = m = 0, r = nc - 1; l < r; ) { + m = (l + r) >> 1; + if (gl[m].encoding < code) + l = m + 1; + else if (gl[m].encoding > code) + r = m - 1; + else + break; + } + + /* + * Go back until we hit the beginning of the glyphs or until + * we find the glyph with a code less than the specified code. + */ + while (m > 0 && gl[m].encoding > code) + m--; + + /* + * Look forward if necessary. + */ + while (m < nc && gl[m].encoding < code) + m++; + + return (m < nc) ? &gl[m] : &gl[nc - 1]; +} + +int +#ifdef __STDC__ +bdf_translate_glyphs(bdf_font_t *font, short dx, short dy, long start, + long end, bdf_callback_t callback, void *data, + int unencoded) +#else +bdf_translate_glyphs(font, dx, dy, start, end, callback, data, unencoded) +bdf_font_t *font; +short dx, dy; +long start, end; +bdf_callback_t callback; +void *data; +int unencoded; +#endif +{ + int resize, diff; + bdf_glyph_t *gp, *sp, *ep; + bdf_callback_struct_t cb; + + if (font == 0 || (dx == 0 && dy == 0)) + return 0; + + if ((unencoded && font->unencoded_used == 0) || font->glyphs_used == 0) + return 0; + + /* + * Call the progress initialization callback. + */ + if (callback != 0) { + cb.reason = BDF_TRANSLATE_START; + cb.total = (end - start) + 1; + cb.current = 0; + (*callback)(&cb, data); + } + + /* + * Locate the first and last glyphs to be shifted. + */ + sp = _bdf_locate_glyph(font, start, unencoded); + ep = _bdf_locate_glyph(font, end, unencoded); + for (resize = 0, gp = sp; sp <= ep; sp++) { + /* + * Call the callback if one was provided. + */ + if (sp != gp && callback != 0) { + cb.reason = BDF_TRANSLATING; + cb.current = (sp->encoding - start) + 1; + (*callback)(&cb, data); + } + + /* + * Apply the X translation. + */ + if (dx != 0) { + sp->bbx.x_offset += dx; + diff = sp->bbx.x_offset - font->bbx.x_offset; + if (sp->bbx.x_offset < font->bbx.x_offset) { + font->bbx.x_offset = sp->bbx.x_offset; + font->bbx.width += MYABS(diff); + resize = 1; + } else if (sp->bbx.width + sp->bbx.x_offset > + font->bbx.width + font->bbx.x_offset) { + font->bbx.width += MYABS(diff); + resize = 1; + } + + /* + * Mark the glyph as modified appropriately. + */ + if (unencoded) + _bdf_set_glyph_modified(font->umod, sp->encoding); + else + _bdf_set_glyph_modified(font->nmod, sp->encoding); + } + + /* + * Apply the Y translation. + */ + if (dy != 0) { + sp->bbx.y_offset += dy; + sp->bbx.descent = -sp->bbx.y_offset; + sp->bbx.ascent = sp->bbx.height - sp->bbx.descent; + diff = sp->bbx.y_offset - font->bbx.y_offset; + if (sp->bbx.y_offset < font->bbx.y_offset) { + font->bbx.y_offset = sp->bbx.y_offset; + font->bbx.descent = -font->bbx.y_offset; + font->bbx.height += MYABS(diff); + resize = 1; + } else if (sp->bbx.ascent > font->bbx.ascent) { + font->bbx.ascent += MYABS(diff); + font->bbx.height += MYABS(diff); + resize = 1; + } + + /* + * Mark the glyph as modified appropriately. + */ + if (unencoded) + _bdf_set_glyph_modified(font->umod, sp->encoding); + else + _bdf_set_glyph_modified(font->nmod, sp->encoding); + } + } + + /* + * Call the callback one more time to make sure the client knows + * this is done. + */ + if (callback != 0 && cb.current < cb.total) { + cb.reason = BDF_TRANSLATING; + cb.current = cb.total; + (*callback)(&cb, data); + } + + if (resize) + font->modified = 1; + + return resize; +} + +static void +#ifdef __STDC__ +_bdf_resize_rotation(bdf_font_t *font, int mul90, short degrees, + bdf_glyph_t *glyph, bdf_bitmap_t *scratch, + unsigned short *width, unsigned short *height) +#else +_bdf_resize_rotation(font, mul90, degrees, glyph, scratch, width, height) +bdf_font_t *font; +int mul90; +short degrees; +bdf_glyph_t *glyph; +bdf_bitmap_t *scratch; +unsigned short *width, *height; +#endif +{ + unsigned short w, h, wd, ht, bytes; + short cx, cy, x1, y1, x2, y2; + double dx1, dy1, dx2, dy2; + + w = h = 0; + + cx = glyph->bbx.width >> 1; + cy = glyph->bbx.height >> 1; + + /* + * Rotate the lower left and upper right corners and check for a potential + * resize. + */ + x1 = 0; + y1 = glyph->bbx.height; + x2 = glyph->bbx.width; + y2 = 0; + + dx1 = (double) (x1 - cx); + dy1 = (double) (y1 - cy); + dx2 = (double) (x2 - cx); + dy2 = (double) (y2 - cx); + + if (mul90) { + x1 = cx + (short) ((dx1 * _bdf_cos_tbl[degrees]) - + (dy1 * _bdf_sin_tbl[degrees])); + y1 = cy + (short) ((dx1 * _bdf_sin_tbl[degrees]) + + (dy1 * _bdf_cos_tbl[degrees])); + x2 = cx + (short) ((dx2 * _bdf_cos_tbl[degrees]) - + (dy2 * _bdf_sin_tbl[degrees])); + y2 = cy + (short) ((dx2 * _bdf_sin_tbl[degrees]) + + (dy2 * _bdf_cos_tbl[degrees])); + } else { + x1 = cx + _bdf_ceiling((dx1 * _bdf_cos_tbl[degrees]) - + (dy1 * _bdf_sin_tbl[degrees])); + y1 = cy + _bdf_ceiling((dx1 * _bdf_sin_tbl[degrees]) + + (dy1 * _bdf_cos_tbl[degrees])); + x2 = cx + _bdf_ceiling((dx2 * _bdf_cos_tbl[degrees]) - + (dy2 * _bdf_sin_tbl[degrees])); + y2 = cy + _bdf_ceiling((dx2 * _bdf_sin_tbl[degrees]) + + (dy2 * _bdf_cos_tbl[degrees])); + } + + wd = MYABS(x2 - x1); + ht = MYABS(y2 - y1); + + w = MAX(wd, w); + h = MAX(ht, h); + + if (wd > font->bbx.width) + font->bbx.width += wd - font->bbx.width; + if (ht > font->bbx.height) { + font->bbx.ascent += ht - font->bbx.height; + font->bbx.height += ht - font->bbx.height; + } + + /* + * Rotate the upper left and lower right corners and check for a potential + * resize. + */ + x1 = 0; + y1 = 0; + x2 = glyph->bbx.width; + y2 = glyph->bbx.height; + + dx1 = (double) (x1 - cx); + dy1 = (double) (y1 - cy); + dx2 = (double) (x2 - cx); + dy2 = (double) (y2 - cx); + + if (mul90) { + x1 = cx + (short) ((dx1 * _bdf_cos_tbl[degrees]) - + (dy1 * _bdf_sin_tbl[degrees])); + y1 = cy + (short) ((dx1 * _bdf_sin_tbl[degrees]) + + (dy1 * _bdf_cos_tbl[degrees])); + x2 = cx + (short) ((dx2 * _bdf_cos_tbl[degrees]) - + (dy2 * _bdf_sin_tbl[degrees])); + y2 = cy + (short) ((dx2 * _bdf_sin_tbl[degrees]) + + (dy2 * _bdf_cos_tbl[degrees])); + } else { + x1 = cx + _bdf_ceiling((dx1 * _bdf_cos_tbl[degrees]) - + (dy1 * _bdf_sin_tbl[degrees])); + y1 = cy + _bdf_ceiling((dx1 * _bdf_sin_tbl[degrees]) + + (dy1 * _bdf_cos_tbl[degrees])); + x2 = cx + _bdf_ceiling((dx2 * _bdf_cos_tbl[degrees]) - + (dy2 * _bdf_sin_tbl[degrees])); + y2 = cy + _bdf_ceiling((dx2 * _bdf_sin_tbl[degrees]) + + (dy2 * _bdf_cos_tbl[degrees])); + } + + wd = MYABS(x2 - x1); + ht = MYABS(y2 - y1); + + w = MAX(wd, w); + h = MAX(ht, h); + + if (wd > font->bbx.width) + font->bbx.width += wd - font->bbx.width; + if (ht > font->bbx.height) { + font->bbx.ascent += ht - font->bbx.height; + font->bbx.height += ht - font->bbx.height; + } + + if (font->bbx.width > scratch->width || + font->bbx.height > scratch->height) { + scratch->width = MAX(font->bbx.width, scratch->width); + scratch->height = MAX(font->bbx.height, scratch->height); + bytes = (((font->bbx.width * font->bpp) + 7) >> 3) * font->bbx.height; + if (scratch->bytes == 0) + scratch->bitmap = (unsigned char *) malloc(bytes); + else + scratch->bitmap = (unsigned char *) + realloc((char *) scratch->bitmap, bytes); + scratch->bytes = bytes; + } + + /* + * Clear the bitmap. + */ + (void) memset((char *) scratch->bitmap, 0, scratch->bytes); + + /* + * Return the new glyph width and height. + */ + *width = w; + *height = h; +} + +int +#ifdef __STDC__ +bdf_rotate_glyphs(bdf_font_t *font, short degrees, long start, + long end, bdf_callback_t callback, void *data, + int unencoded) +#else +bdf_rotate_glyphs(font, degrees, start, end, callback, data, unencoded) +bdf_font_t *font; +short degrees; +long start, end; +bdf_callback_t callback; +void *data; +int unencoded; +#endif +{ + int mul90, bpr, sbpr; + unsigned short wd, ht, si, di, byte, col; + short x, y, cx, cy, nx, ny, ox, oy, shiftx, shifty; + bdf_glyph_t *gp, *sp, *ep; + unsigned char *masks; + double dx, dy; + bdf_bitmap_t scratch; + bdf_callback_struct_t cb; + + if (font == 0 || (unencoded && font->unencoded_used == 0) || + font->glyphs_used == 0) + return 0; + + while (degrees < 0) + degrees += 360; + while (degrees >= 360) + degrees -= 360; + + if (degrees == 0) + return 0; + + mul90 = ((degrees % 90) == 0) ? 1 : 0; + + masks = 0; + switch (font->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + /* + * Initialize the scratch bitmap. + */ + (void) memset((char *) &scratch, 0, sizeof(bdf_bitmap_t)); + + /* + * Call the progress initialization callback. + */ + if (callback != 0) { + cb.reason = BDF_ROTATE_START; + cb.total = (end - start) + 1; + cb.current = 0; + (*callback)(&cb, data); + } + + sp = _bdf_locate_glyph(font, start, unencoded); + ep = _bdf_locate_glyph(font, end, unencoded); + for (gp = sp; sp <= ep; sp++) { + /* + * Call the callback if one was provided. + */ + if (sp != gp && callback != 0) { + cb.reason = BDF_ROTATING; + cb.current = (sp->encoding - start) + 1; + (*callback)(&cb, data); + } + + /* + * Resize the bitmap, adjust the font bounding box, and get the new + * glyph width and height. + */ + _bdf_resize_rotation(font, mul90, degrees, sp, &scratch, &wd, &ht); + + cx = sp->bbx.width >> 1; + cy = sp->bbx.height >> 1; + + shiftx = shifty = 0; + sbpr = ((wd * font->bpp) + 7) >> 3; + bpr = ((sp->bbx.width * font->bpp) + 7) >> 3; + for (y = 0; y < sp->bbx.height; y++) { + for (col = x = 0; x < sp->bbx.width; x++, col += font->bpp) { + si = (col & 7) / font->bpp; + byte = sp->bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + dx = (double) (x - cx); + dy = (double) (y - cy); + if (mul90) { + nx = cx + (short) ((dx * _bdf_cos_tbl[degrees]) - + (dy * _bdf_sin_tbl[degrees])); + ny = cy + (short) ((dx * _bdf_sin_tbl[degrees]) + + (dy * _bdf_cos_tbl[degrees])); + } else { + nx = cx + _bdf_ceiling((dx * _bdf_cos_tbl[degrees]) - + (dy * _bdf_sin_tbl[degrees])); + ny = cy + _bdf_ceiling((dx * _bdf_sin_tbl[degrees]) + + (dy * _bdf_cos_tbl[degrees])); + } + if (nx < 0) { + shiftx = MIN(shiftx, nx); + nx += wd; + } else if (nx >= wd) { + ox = (nx - wd) + 1; + shiftx = MAX(shiftx, ox); + nx -= wd; + } + if (ny < 0) { + shifty = MIN(shifty, ny); + ny += ht; + } else if (ny >= ht) { + oy = (ny - ht) + 1; + shifty = MAX(shifty, oy); + ny -= ht; + } + nx *= font->bpp; + di = (nx & 7) / font->bpp; + if (di < si) + byte <<= (si - di) * font->bpp; + else if (di > si) + byte >>= (di - si) * font->bpp; + scratch.bitmap[(ny * sbpr) + (nx >> 3)] |= byte; + } + } + } + /* + * Resize the glyph bitmap if necessary. + */ + if (wd != sp->bbx.width || ht != sp->bbx.height) { + sp->bbx.width = wd; + sp->bbx.height = ht; + sp->bbx.ascent = ht - sp->bbx.descent; + sp->bytes = (((wd * font->bpp) + 7) >> 3) * ht; + sp->bitmap = (unsigned char *) + realloc((char *) sp->bitmap, sp->bytes); + } + (void) memset((char *) sp->bitmap, 0, sp->bytes); + + /* + * Copy the glyph from the scratch area to the glyph bitmap, + * adjusting for any shift values encountered. + */ + bpr = ((sp->bbx.width * font->bpp) + 7) >> 3; + for (y = 0; y < sp->bbx.height; y++) { + for (col = x = 0; x < sp->bbx.width; x++, col += font->bpp) { + si = (col & 7) / font->bpp; + byte = scratch.bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + nx = x - shiftx; + ny = y - shifty; + if (nx < 0) + nx += sp->bbx.width; + else if (nx >= sp->bbx.width) + nx -= sp->bbx.width; + if (ny < 0) + ny += sp->bbx.height; + else if (ny >= sp->bbx.height) + ny -= sp->bbx.height; + nx *= font->bpp; + di = (nx & 7) / font->bpp; + if (di < si) + byte <<= (si - di) * font->bpp; + else if (di > si) + byte >>= (di - si) * font->bpp; + sp->bitmap[(ny * bpr) + (nx >> 3)] |= byte; + } + } + } + /* + * Mark the glyph as modified. + */ + if (unencoded) + _bdf_set_glyph_modified(font->umod, sp->encoding); + else + _bdf_set_glyph_modified(font->nmod, sp->encoding); + } + + /* + * Call the callback one more time to make sure the client knows + * this is done. + */ + if (callback != 0 && cb.current < cb.total) { + cb.reason = BDF_TRANSLATING; + cb.current = cb.total; + (*callback)(&cb, data); + } + + if (scratch.bytes > 0) + free((char *) scratch.bitmap); + + /* + * Rotations always change things, so just return a value indicating this. + */ + font->modified = 1; + return 1; +} + +static void +#ifdef __STDC__ +_bdf_resize_shear(bdf_font_t *font, int neg, short degrees, + bdf_glyph_t *glyph, bdf_bitmap_t *scratch, + unsigned short *width, unsigned short *height) +#else +_bdf_resize_shear(font, neg, degrees, glyph, scratch, width, height) +bdf_font_t *font; +int neg; +short degrees; +bdf_glyph_t *glyph; +bdf_bitmap_t *scratch; +unsigned short *width, *height; +#endif +{ + unsigned short wd, w, bytes; + short x1, y1, x2, y2; + + w = 0; + *height = glyph->bbx.height; + + /* + * Shear the lower left and upper right corners and check for a potential + * resize. + */ + x1 = 0; + y1 = glyph->bbx.height; + x2 = glyph->bbx.width; + y2 = 0; + + if (neg) { + x1 += (short) ((double) y1 * _bdf_tan_tbl[degrees]); + x2 += (short) ((double) y2 * _bdf_tan_tbl[degrees]); + } else { + x1 += (short) ((double) (glyph->bbx.height - y1) * + _bdf_tan_tbl[degrees]); + x2 += (short) ((double) (glyph->bbx.height - y2) * + _bdf_tan_tbl[degrees]); + } + + wd = MYABS(x2 - x1); + w = MAX(w, wd); + + if (wd > font->bbx.width) + font->bbx.width += wd - font->bbx.width; + + /* + * Shear the upper left and lower right corners and check for a potential + * resize. + */ + x1 = 0; + y1 = 0; + x2 = glyph->bbx.width; + y2 = glyph->bbx.height; + + if (neg) { + x1 += (short) ((double) y1 * _bdf_tan_tbl[degrees]); + x2 += (short) ((double) y2 * _bdf_tan_tbl[degrees]); + } else { + x1 += (short) ((double) (glyph->bbx.height - y1) * + _bdf_tan_tbl[degrees]); + x2 += (short) ((double) (glyph->bbx.height - y2) * + _bdf_tan_tbl[degrees]); + } + + wd = MYABS(x2 - x1); + w = MAX(w, wd); + + if (wd > font->bbx.width) + font->bbx.width += wd - font->bbx.width; + + if (font->bbx.width > scratch->width || + font->bbx.height > scratch->height) { + scratch->width = MAX(font->bbx.width, scratch->width); + scratch->height = MAX(font->bbx.height, scratch->height); + bytes = (((font->bbx.width * font->bpp) + 7) >> 3) * font->bbx.height; + if (scratch->bytes == 0) + scratch->bitmap = (unsigned char *) malloc(bytes); + else + scratch->bitmap = (unsigned char *) + realloc((char *) scratch->bitmap, bytes); + scratch->bytes = bytes; + } + + /* + * Clear the bitmap. + */ + (void) memset((char *) scratch->bitmap, 0, scratch->bytes); + + /* + * Return the new glyph width. + */ + *width = w; +} + +int +#ifdef __STDC__ +bdf_shear_glyphs(bdf_font_t *font, short degrees, long start, + long end, bdf_callback_t callback, void *data, + int unencoded) +#else +bdf_shear_glyphs(font, degrees, start, end, callback, data, unencoded) +bdf_font_t *font; +short degrees; +long start, end; +bdf_callback_t callback; +void *data; +int unencoded; +#endif +{ + int neg, bpr, sbpr; + unsigned short wd, ht, si, di, byte, col; + short x, y, nx, shiftx, ox; + bdf_glyph_t *gp, *sp, *ep; + unsigned char *masks; + bdf_bitmap_t scratch; + bdf_callback_struct_t cb; + + if (font == 0 || (unencoded && font->unencoded_used == 0) || + font->glyphs_used == 0) + return 0; + + if (degrees == 0 || degrees < -45 || degrees > 45) + return 0; + + if ((neg = (degrees < 0))) + degrees = -degrees; + + masks = 0; + switch (font->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + /* + * Initialize the scratch bitmap. + */ + (void) memset((char *) &scratch, 0, sizeof(bdf_bitmap_t)); + + /* + * Call the progress initialization callback. + */ + if (callback != 0) { + cb.reason = BDF_SHEAR_START; + cb.total = (end - start) + 1; + cb.current = 0; + (*callback)(&cb, data); + } + + sp = _bdf_locate_glyph(font, start, unencoded); + ep = _bdf_locate_glyph(font, end, unencoded); + for (gp = sp; sp <= ep; sp++) { + /* + * Call the callback if one was provided. + */ + if (sp != gp && callback != 0) { + cb.reason = BDF_SHEARING; + cb.current = (sp->encoding - start) + 1; + (*callback)(&cb, data); + } + + /* + * Resize the bitmap, adjust the font bounding box, and get the new + * glyph width and height. + */ + _bdf_resize_shear(font, neg, degrees, sp, &scratch, &wd, &ht); + + shiftx = 0; + sbpr = ((wd * font->bpp) + 7) >> 3; + bpr = ((sp->bbx.width * font->bpp) + 7) >> 3; + for (y = 0; y < sp->bbx.height; y++) { + for (col = x = 0; x < sp->bbx.width; x++, col += font->bpp) { + si = (col & 7) / font->bpp; + byte = sp->bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + if (neg) + nx = x + (short) ((double) y * _bdf_tan_tbl[degrees]); + else + nx = x + (short) ((double) (sp->bbx.height - y) * + _bdf_tan_tbl[degrees]); + + if (nx < 0) { + shiftx = MIN(shiftx, nx); + nx += wd; + } else if (nx >= wd) { + ox = (nx - wd) + 1; + shiftx = MAX(shiftx, ox); + nx -= wd; + } + nx *= font->bpp; + di = (nx & 7) / font->bpp; + if (di < si) + byte <<= (si - di) * font->bpp; + else if (di > si) + byte >>= (di - si) * font->bpp; + scratch.bitmap[(y * sbpr) + (nx >> 3)] |= byte; + } + } + } + /* + * Resize the glyph bitmap if necessary. + */ + if (wd != sp->bbx.width || ht != sp->bbx.height) { + sp->bbx.width = wd; + sp->bbx.height = ht; + sp->bbx.ascent = ht - sp->bbx.descent; + sp->bytes = (((wd * font->bpp) + 7) >> 3) * ht; + sp->bitmap = (unsigned char *) + realloc((char *) sp->bitmap, sp->bytes); + } + (void) memset((char *) sp->bitmap, 0, sp->bytes); + + /* + * Copy the glyph from the scratch area to the glyph bitmap, + * adjusting for any shift values encountered. + */ + bpr = ((sp->bbx.width * font->bpp) + 7) >> 3; + for (y = 0; y < sp->bbx.height; y++) { + for (col = x = 0; x < sp->bbx.width; x++, col += font->bpp) { + si = (col & 7) / font->bpp; + byte = scratch.bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + nx = x - shiftx; + if (nx < 0) + nx += sp->bbx.width; + else if (nx >= sp->bbx.width) + nx -= sp->bbx.width; + nx *= font->bpp; + di = (nx & 7) / font->bpp; + if (di < si) + byte <<= (si - di) * font->bpp; + else if (di > si) + byte >>= (di - si) * font->bpp; + sp->bitmap[(y * bpr) + (nx >> 3)] |= byte; + } + } + } + /* + * Mark the glyph as modified. + */ + if (unencoded) + _bdf_set_glyph_modified(font->umod, sp->encoding); + else + _bdf_set_glyph_modified(font->nmod, sp->encoding); + } + + /* + * Call the callback one more time to make sure the client knows + * this is done. + */ + if (callback != 0 && cb.current < cb.total) { + cb.reason = BDF_TRANSLATING; + cb.current = cb.total; + (*callback)(&cb, data); + } + + if (scratch.bytes > 0) + free((char *) scratch.bitmap); + + /* + * Rotations always change things, so just return a value indicating this. + */ + font->modified = 1; + return 1; +} + +static void +#ifdef __STDC__ +_bdf_widen_by(bdf_font_t *f, bdf_glyph_t *g, bdf_bitmap_t *s, int n) +#else +_bdf_widen_by(f, g, s, n) +bdf_font_t *f; +bdf_glyph_t *g; +bdf_bitmap_t *s; +int n; +#endif +{ + int bytes, sbpr, dbpr, col; + short x, y, si, di; + unsigned char *bmap, *masks; + + masks = 0; + switch (f->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + s->height = g->bbx.height; + s->width = g->bbx.width + n; + + bytes = (((s->width * f->bpp) + 7) >> 3) * s->height; + + if (s->bytes == 0) + s->bitmap = (unsigned char *) malloc(bytes); + else + s->bitmap = (unsigned char *) + realloc((char *) s->bitmap, bytes); + s->bytes = bytes; + + (void) memset((char *) s->bitmap, 0, s->bytes); + + /* + * Copy the glyph bitmap to the scratch area, and then swap the bitmaps. + */ + sbpr = ((g->bbx.width * f->bpp) + 7) >> 3; + dbpr = ((s->width * f->bpp) + 7) >> 3; + for (y = 0; y < g->bbx.height; y++) { + for (col = x = 0; x < g->bbx.width; x++, col += f->bpp) { + si = (col & 7) / f->bpp; + bytes = g->bitmap[(y * sbpr) + (col >> 3)] & masks[si]; + if (bytes) { + di = ((x * f->bpp) & 7) / f->bpp; + if (di < si) + bytes <<= (si - di) * f->bpp; + else if (di > si) + bytes >>= (di - si) * f->bpp; + s->bitmap[(y * dbpr) + (col >> 3)] |= bytes; + } + } + } + g->bbx.width = s->width; + + /* + * Swap the bytes and bitmap fields from the scratch area and the glyph. + */ + bytes = g->bytes; + g->bytes = s->bytes; + s->bytes = bytes; + + bmap = g->bitmap; + g->bitmap = s->bitmap; + s->bitmap = bmap; +} + +int +#ifdef __STDC__ +bdf_embolden_glyphs(bdf_font_t *font, long start, long end, + bdf_callback_t callback, void *data, int unencoded, + int *resize) +#else +bdf_embolden_glyphs(font, start, end, callback, data, unencoded, resize) +bdf_font_t *font; +long start, end; +bdf_callback_t callback; +void *data; +int unencoded, *resize; +#endif +{ + int mod, gmod, bpr; + short x, y; + unsigned short si, di, b1, b2, col; + unsigned char *masks; + bdf_glyph_t *gp, *sp, *ep; + bdf_bitmap_t scratch; + bdf_callback_struct_t cb; + + if (font == 0 || (unencoded && font->unencoded_used == 0) || + font->glyphs_used == 0) + return 0; + + /* + * Initialize the scratch bitmap which may be needed. + */ + (void) memset((char *) &scratch, 0, sizeof(bdf_bitmap_t)); + + mod = 0; + gp = 0; + + masks = 0; + switch (font->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + /* + * Call the progress initialization callback. + */ + if (callback != 0) { + cb.reason = BDF_EMBOLDEN_START; + cb.total = (end - start) + 1; + cb.current = 0; + (*callback)(&cb, data); + } + + /* + * Initialize the resize flag for the caller. + */ + *resize = 0; + + sp = _bdf_locate_glyph(font, start, unencoded); + ep = _bdf_locate_glyph(font, end, unencoded); + for (; sp <= ep; sp++) { + /* + * Call the callback if one was provided. + */ + if (sp != gp && callback != 0) { + cb.reason = BDF_EMBOLDENING; + cb.current = (sp->encoding - start) + 1; + (*callback)(&cb, data); + } + + if (font->spacing == BDF_PROPORTIONAL || + (font->spacing == BDF_MONOWIDTH && + sp->bbx.width < font->bbx.width)) { + /* + * Only widen the glyph if it is within reason. + */ + _bdf_widen_by(font, sp, &scratch, 1); + + if (sp->bbx.width > font->bbx.width) { + /* + * Bump the font width up by the difference. + */ + font->bbx.width += sp->bbx.width - font->bbx.width; + *resize = 1; + } + } + + gmod = 0; + bpr = ((sp->bbx.width * font->bpp) + 7) >> 3; + for (y = 0; y < sp->bbx.height; y++) { + col = (sp->bbx.width - 1) * font->bpp; + for (x = sp->bbx.width - 1; x > 0; x--, col -= font->bpp) { + si = (col & 7) / font->bpp; + di = ((col - font->bpp) & 7) / font->bpp; + b1 = (x == sp->bbx.width) ? 0 : + sp->bitmap[(y * bpr) + (col >> 3)] & masks[si]; + b2 = sp->bitmap[(y * bpr) + ((col - font->bpp) >> 3)] & + masks[di]; + if (!b1 && b2) { + if (di < si) + b2 >>= (si - di) * font->bpp; + else if (di > si) + b2 <<= (di - si) * font->bpp; + sp->bitmap[(y * bpr) + (col >> 3)] |= b2; + gmod = mod = 1; + } + } + } + /* + * Mark the glyph as modified. + */ + if (gmod) { + if (unencoded) + _bdf_set_glyph_modified(font->umod, sp->encoding); + else + _bdf_set_glyph_modified(font->nmod, sp->encoding); + } + } + + /* + * Call the callback one more time to make sure the client knows + * this is done. + */ + if (callback != 0 && cb.current < cb.total) { + cb.reason = BDF_EMBOLDENING; + cb.current = cb.total; + (*callback)(&cb, data); + } + + /* + * Deallocate the scratch bitmap if necessary. + */ + if (scratch.bytes > 0) + free((char *) scratch.bitmap); + + font->modified = mod; + + return mod; +} + +static int _endian = 1; +static char *little_endian = (char *) &_endian; + +int +#ifdef __STDC__ +bdf_little_endian(void) +#else +bdf_little_endian() +#endif +{ + return *little_endian; +} diff --git a/engines/sci/tools/bdf.h b/engines/sci/tools/bdf.h new file mode 100644 index 0000000000..b9895c985f --- /dev/null +++ b/engines/sci/tools/bdf.h @@ -0,0 +1,675 @@ +/* + * Copyright 2001 Computing Research Labs, New Mexico State University + * + * 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. + */ +#ifndef _h_bdf +#define _h_bdf + +/* + * $Id: bdf.h 1284 2004-04-02 07:42:44Z jameson $ + */ + +#include +#include +#ifndef __digital__ +#include +#endif +#include + +#ifdef HAVE_XLIB +#include +#include +#include +#endif /* HAVE_XLIB */ + +#ifdef HAVE_FREETYPE +#include +#include +#endif /* HAVE_FREETYPE */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * A macro for prototypes. + */ +#undef __ +#ifdef __STDC__ +#define __(x) x +#else +#define __(x) () +#endif + +/************************************************************************** + * + * BDF font options macros and types. + * + **************************************************************************/ + +#define BDF_UNIX_EOL 1 /* Save fonts with Unix LF. */ +#define BDF_DOS_EOL 2 /* Save fonts with DOS CRLF. */ +#define BDF_MAC_EOL 3 /* Save fonts with Mac CR. */ + +#define BDF_CORRECT_METRICS 0x01 /* Correct invalid metrics when loading. */ +#define BDF_KEEP_COMMENTS 0x02 /* Preserve the font comments. */ +#define BDF_KEEP_UNENCODED 0x04 /* Keep the unencoded glyphs. */ +#define BDF_PROPORTIONAL 0x08 /* Font has proportional spacing. */ +#define BDF_MONOWIDTH 0x10 /* Font has mono width. */ +#define BDF_CHARCELL 0x20 /* Font has charcell spacing. */ + +#define BDF_ALL_SPACING (BDF_PROPORTIONAL|BDF_MONOWIDTH|BDF_CHARCELL) + +#define BDF_DEFAULT_LOAD_OPTIONS \ + (BDF_CORRECT_METRICS|BDF_KEEP_COMMENTS|BDF_KEEP_UNENCODED|BDF_PROPORTIONAL) + +typedef struct { + int ttf_hint; + int correct_metrics; + int keep_unencoded; + int keep_comments; + int pad_cells; + int font_spacing; + long point_size; + unsigned long resolution_x; + unsigned long resolution_y; + int bits_per_pixel; + int eol; +} bdf_options_t; + +/* + * Callback function type for unknown configuration options. + */ +typedef int (*bdf_options_callback_t) __((bdf_options_t *opts, + char **params, + unsigned long nparams, + void *client_data)); + +/************************************************************************** + * + * BDF font property macros and types. + * + **************************************************************************/ + +#define BDF_ATOM 1 +#define BDF_INTEGER 2 +#define BDF_CARDINAL 3 + +/* + * This structure represents a particular property of a font. + * There are a set of defaults and each font has their own. + */ +typedef struct { + char *name; /* Name of the property. */ + int format; /* Format of the property. */ + int builtin; /* A builtin property. */ + union { + char *atom; + long int32; + unsigned long card32; + } value; /* Value of the property. */ +} bdf_property_t; + +/************************************************************************** + * + * SBIT metrics specific structures. + * + **************************************************************************/ + +/* + * Boolean flags for SBIT metrics files. + */ +#define BDF_SBIT_MONO_ADVANCE 0x0001 +#define BDF_SBIT_ADD_EBLC 0x0002 +#define BDF_SBIT_APPLE_COMPAT 0x0004 + +/* + * Direction macros (inclusive, can be combined). + */ +#define BDF_SBIT_HORIZONTAL 0x0008 +#define BDF_SBIT_VERTICAL 0x0010 + +/* + * Bitmap storage options (exclusive, cannot be combined). + */ +#define BDF_SBIT_STORE_SMALL 0x0020 +#define BDF_SBIT_STORE_FAST 0x0040 + +typedef struct { + short cnum; /* Caret slope numerator. */ + short cdenom; /* Caret slope denominator. */ + short coff; /* Caret offset. */ + short sx; /* Scaled version horizontal PPEM size. */ + short sy; /* Scaled version vertical PPEM size (optional).*/ + short flags; /* Booleans and other non-numeric values. */ +} bdf_sbit_t; + +/************************************************************************** + * + * BDF opaque undo information types. + * + **************************************************************************/ + +typedef struct _bdf_undo_struct *bdf_undo_t; + +/************************************************************************** + * + * BDF font metric and glyph types. + * + **************************************************************************/ + +/* + * A general bitmap type, mostly used when the glyph bitmap is being edited. + */ +typedef struct { + short x; + short y; + unsigned short width; + unsigned short height; + unsigned short bpp; + unsigned short pad; + unsigned char *bitmap; + unsigned long bytes; +} bdf_bitmap_t; + +typedef struct { + int font_spacing; + unsigned short swidth; + unsigned short dwidth; + unsigned short width; + unsigned short height; + short x_offset; + short y_offset; + short ascent; + short descent; +} bdf_metrics_t; + +typedef struct { + unsigned short width; + unsigned short height; + short x_offset; + short y_offset; + short ascent; + short descent; +} bdf_bbx_t; + +typedef struct { + char *name; /* Glyph name. */ + long encoding; /* Glyph encoding. */ + unsigned short swidth; /* Scalable width. */ + unsigned short dwidth; /* Device width. */ + bdf_bbx_t bbx; /* Glyph bounding box. */ + unsigned char *bitmap; /* Glyph bitmap. */ + unsigned short bytes; /* Number of bytes used for the bitmap. */ +} bdf_glyph_t; + +typedef struct { + unsigned short pad; /* Pad to 4-byte boundary. */ + unsigned short bpp; /* Bits per pixel. */ + long start; /* Beginning encoding value of glyphs. */ + long end; /* Ending encoding value of glyphs. */ + bdf_glyph_t *glyphs; /* Glyphs themselves. */ + unsigned long glyphs_size; /* Glyph structures allocated. */ + unsigned long glyphs_used; /* Glyph structures used. */ + bdf_bbx_t bbx; /* Overall bounding box of glyphs. */ +} bdf_glyphlist_t; + +typedef struct { + char *name; /* Name of the font. */ + bdf_bbx_t bbx; /* Font bounding box. */ + + long point_size; /* Point size of the font. */ + unsigned long resolution_x; /* Font horizontal resolution. */ + unsigned long resolution_y; /* Font vertical resolution. */ + + int hbf; /* Font came from an HBF font. */ + + int spacing; /* Font spacing value. */ + + unsigned short monowidth; /* Logical width for monowidth font. */ + + long default_glyph; /* Encoding of the default glyph. */ + + long font_ascent; /* Font ascent. */ + long font_descent; /* Font descent. */ + + long glyphs_size; /* Glyph structures allocated. */ + long glyphs_used; /* Glyph structures used. */ + bdf_glyph_t *glyphs; /* Glyphs themselves. */ + + long unencoded_size; /* Unencoded glyph structures allocated. */ + long unencoded_used; /* Unencoded glyph structures used. */ + bdf_glyph_t *unencoded; /* Unencoded glyphs themselves. */ + + unsigned long props_size; /* Font properties allocated. */ + unsigned long props_used; /* Font properties used. */ + bdf_property_t *props; /* Font properties themselves. */ + + char *comments; /* Font comments. */ + unsigned long comments_len; /* Length of comment string. */ + + char *acmsgs; /* Auto-correction messages. */ + unsigned long acmsgs_len; /* Length of auto-correction messages. */ + + bdf_glyphlist_t overflow; /* Storage used for glyph insertion. */ + + void *internal; /* Internal data for the font. */ + + unsigned long nmod[2048]; /* Bitmap indicating modified glyphs. */ + unsigned long umod[2048]; /* Bitmap indicating modified unencoded. */ + + unsigned short modified; /* Boolean indicating font modified. */ + unsigned short bpp; /* Bits per pixel. */ + + bdf_sbit_t *sbits; /* Associcated SBIT metrics. */ + unsigned long sbits_used; /* Number of SBIT metrics entries. */ + unsigned long sbits_size; /* Amount of entries allocated. */ + + bdf_undo_t *undo_stack; /* Record of undoable operations. */ + unsigned long undo_used; /* Amount of undo stack used. */ + unsigned long undo_size; /* Amount of undo stack allocated. */ +} bdf_font_t; + +/************************************************************************** + * + * BDF glyph grid structures for editing glyph bitmaps. + * + **************************************************************************/ + +typedef struct { + char *name; + long encoding; /* The glyph encoding. */ + unsigned short unencoded; /* Whether the glyph was unencoded. */ + unsigned short bpp; /* Bits per pixel. */ + int spacing; /* Font spacing. */ + long resolution_x; /* Horizontal resolution. */ + long resolution_y; /* Vertical resolution. */ + unsigned long point_size; /* Font point size. */ + unsigned short swidth; /* Scalable width. */ + unsigned short dwidth; /* Device width. */ + bdf_bbx_t font_bbx; /* Font bounding box. */ + bdf_bbx_t glyph_bbx; /* Glyph bounding box. */ + unsigned char *bitmap; /* The grid bitmap. */ + unsigned short bytes; /* Number of bytes in the grid bitmap. */ + short grid_width; /* Width of the grid. */ + short grid_height; /* Height of the grid. */ + short base_x; /* Baseline X coordinate. */ + short base_y; /* Baseline Y coordinate. */ + short glyph_x; /* Top-left X position of glyph. */ + short glyph_y; /* Top-left Y position of glyph. */ + unsigned short modified; /* Flag indicating if bitmap modified. */ + short cap_height; /* Font CAP_HEIGHT if it exists. */ + short x_height; /* Font X_HEIGHT if it exists. */ + bdf_bitmap_t sel; /* Selected portion of the glyph bitmap.*/ +} bdf_glyph_grid_t; + +/************************************************************************** + * + * Types for load/save callbacks. + * + **************************************************************************/ + +/* + * Callback reasons. + */ +#define BDF_LOAD_START 1 +#define BDF_LOADING 2 +#define BDF_SAVE_START 3 +#define BDF_SAVING 4 +#define BDF_TRANSLATE_START 5 +#define BDF_TRANSLATING 6 +#define BDF_ROTATE_START 7 +#define BDF_ROTATING 8 +#define BDF_SHEAR_START 9 +#define BDF_SHEARING 10 +#define BDF_GLYPH_NAME_START 11 +#define BDF_GLYPH_NAME 12 +#define BDF_EXPORT_START 13 +#define BDF_EXPORTING 14 +#define BDF_EMBOLDEN_START 15 +#define BDF_EMBOLDENING 16 +#define BDF_WARNING 20 +#define BDF_ERROR 21 + +/* + * Error codes. + */ +#define BDF_OK 0 +#define BDF_MISSING_START -1 +#define BDF_MISSING_FONTNAME -2 +#define BDF_MISSING_SIZE -3 +#define BDF_MISSING_FONTBBX -4 +#define BDF_MISSING_CHARS -5 +#define BDF_MISSING_STARTCHAR -6 +#define BDF_MISSING_ENCODING -7 +#define BDF_MISSING_BBX -8 + +#define BDF_NOT_CONSOLE_FONT -10 +#define BDF_NOT_MF_FONT -11 +#define BDF_NOT_PSF_FONT -12 +#define BDF_EMPTY_FONT -99 +#define BDF_INVALID_LINE -100 + +typedef struct { + unsigned long reason; + unsigned long current; + unsigned long total; + unsigned long errlineno; +} bdf_callback_struct_t; + +typedef void (*bdf_callback_t) __((bdf_callback_struct_t *call_data, + void *client_data)); + +/************************************************************************** + * + * BDF font API. + * + **************************************************************************/ + +/* + * Startup and shutdown functions. + */ +extern void bdf_setup __((void)); +extern void bdf_cleanup __((void)); + +/* + * Configuration file loading and saving. + */ +extern void bdf_load_options __((FILE *in, bdf_options_t *opts, + bdf_options_callback_t callback, + void *client_data)); +extern void bdf_save_options __((FILE *out, bdf_options_t *opts)); + +/* + * Font options functions. + */ +extern void bdf_default_options __((bdf_options_t *opts)); + +/* + * Font load, create, save and free functions. + */ +extern bdf_font_t *bdf_new_font __((char *name, long point_size, + long resolution_x, long resolution_y, + long spacing, int bpp)); +extern bdf_font_t *bdf_load_font __((FILE *in, bdf_options_t *opts, + bdf_callback_t callback, void *data)); +extern bdf_font_t *bdf_load_hbf_font __((char *filename, bdf_options_t *opts, + bdf_callback_t callback, void *data)); + +#ifdef HAVE_XLIB +extern bdf_font_t *bdf_load_server_font __((Display *d, XFontStruct *f, + char *name, bdf_options_t *opts, + bdf_callback_t callback, + void *data)); +#endif /* HAVE_XLIB */ + +extern int bdf_load_console_font __((FILE *in, bdf_options_t *opts, + bdf_callback_t callback, void *data, + bdf_font_t *fonts[3], int *nfonts)); + +extern int bdf_load_mf_font __((FILE *in, bdf_options_t *opts, + bdf_callback_t callback, void *data, + bdf_font_t **font)); + +extern void bdf_save_font __((FILE *out, bdf_font_t *font, + bdf_options_t *opts, bdf_callback_t callback, + void *data)); + +extern void bdf_save_sbit_metrics __((FILE *out, bdf_font_t *font, + bdf_options_t *opts, char *appname)); + +extern void bdf_export_hex __((FILE *out, bdf_font_t *font, + bdf_callback_t callback, void *data)); + +extern int bdf_export_psf __((FILE *out, bdf_font_t *font)); + +extern void bdf_free_font __((bdf_font_t *font)); + +#ifdef HAVE_FREETYPE + +/* + * TrueType related macros and functions. + */ + +/* + * ID numbers of the strings that can appear in a TrueType font. + */ +#define BDFTTF_COPYRIGHT_STRING 0 +#define BDFTTF_FAMILY_STRING 1 +#define BDFTTF_SUBFAMILY_STRING 2 +#define BDFTTF_UNIQUEID_STRING 3 +#define BDFTTF_FULLNAME_STRING 4 +#define BDFTTF_VENDOR_STRING 5 +#define BDFTTF_POSTSCRIPT_STRING 6 +#define BDFTTF_TRADEMARK_STRING 7 + +extern char *bdfttf_platform_name __((short pid)); +extern char *bdfttf_encoding_name __((short pid, short eid)); +extern int bdfttf_get_english_string __((TT_Face face, int nameID, + int dash_to_space, char *name)); + +extern int bdfttf_load_font __((TT_Face face, TT_Face_Properties *properties, + short pid, short eid, bdf_options_t *opts, + bdf_callback_t callback, void *data, + bdf_font_t **font)); + +#endif /* HAVE_FREETYPE */ + +/* + * FON/FNT related functions. + */ + +/* + * String ID numbers for FON/FNT fonts. + */ +#define BDFFNT_COPYRIGHT 1 +#define BDFFNT_TYPEFACE 2 + +/* + * Opaque font type. + */ +typedef struct _bdffnt_font_t *bdffnt_font_t; + +extern int bdffnt_open_font __((char *path, bdffnt_font_t *font)); +extern void bdffnt_close_font __((bdffnt_font_t font)); +extern int bdffnt_font_count __((bdffnt_font_t font)); +extern int bdffnt_get_copyright __((bdffnt_font_t font, unsigned long fontID, + unsigned char *string)); +extern int bdffnt_get_facename __((bdffnt_font_t font, unsigned long fontID, + int for_xlfd, unsigned char *string)); +extern int bdffnt_char_count __((bdffnt_font_t font, unsigned long fontID)); +extern int bdffnt_font_pointsize __((bdffnt_font_t font, + unsigned long fontID)); +extern int bdffnt_load_font __((bdffnt_font_t font, unsigned long fontID, + bdf_callback_t callback, void *data, + bdf_font_t **out)); + +/* + * Font property functions. + */ +extern void bdf_create_property __((char *name, int type)); +extern bdf_property_t *bdf_get_property __((char *name)); +extern unsigned long bdf_property_list __((bdf_property_t **props)); + +extern void bdf_add_font_property __((bdf_font_t *font, + bdf_property_t *property)); +extern void bdf_delete_font_property __((bdf_font_t *font, char *name)); +extern bdf_property_t *bdf_get_font_property __((bdf_font_t *font, + const char *name)); +extern unsigned long bdf_font_property_list __((bdf_font_t *font, + bdf_property_t **props)); + +/* + * Font comment functions. + */ +extern int bdf_replace_comments __((bdf_font_t *font, char *comments, + unsigned long comments_len)); + +/* + * Other miscellaneous functions. + */ +extern void bdf_set_default_metrics __((bdf_font_t *font)); + +/* + * Font glyph editing functions. + */ +extern int bdf_glyph_modified __((bdf_font_t *font, long which, + int unencoded)); + +extern void bdf_copy_glyphs __((bdf_font_t *font, long start, long end, + bdf_glyphlist_t *glyphs, int unencoded)); + +extern void bdf_delete_glyphs __((bdf_font_t *font, long start, long end, + int unencoded)); + +extern int bdf_insert_glyphs __((bdf_font_t *font, long start, + bdf_glyphlist_t *glyphs)); + +extern int bdf_replace_glyphs __((bdf_font_t *font, long start, + bdf_glyphlist_t *glyphs, int unencoded)); + +extern int bdf_merge_glyphs __((bdf_font_t *font, long start, + bdf_glyphlist_t *glyphs, int unencoded)); + +/************************************************************************** + * + * Other API functions. + * + **************************************************************************/ + +extern int bdf_set_font_bbx __((bdf_font_t *font, bdf_metrics_t *metrics)); + +extern void bdf_set_modified __((bdf_font_t *font, int modified)); + +extern int bdf_has_xlfd_name __((bdf_font_t *font)); + +extern char *bdf_make_xlfd_name __((bdf_font_t *font, char *foundry, + char *family)); + +extern void bdf_update_name_from_properties __((bdf_font_t *font)); + +extern void bdf_update_properties_from_name __((bdf_font_t *font)); + +extern int bdf_update_average_width __((bdf_font_t *font)); + +extern int bdf_set_unicode_glyph_names __((FILE *in, bdf_font_t *font, + bdf_callback_t callback)); + +extern int bdf_set_adobe_glyph_names __((FILE *in, bdf_font_t *font, + bdf_callback_t callback)); + +extern int bdf_set_glyph_code_names __((int prefix, bdf_font_t *font, + bdf_callback_t callback)); + +/************************************************************************** + * + * Glyph grid API. + * + **************************************************************************/ + +/* + * Glyph grid allocation and deallocation functions. + */ +extern bdf_glyph_grid_t *bdf_make_glyph_grid __((bdf_font_t *font, + long code, + int unencoded)); +extern void bdf_free_glyph_grid __((bdf_glyph_grid_t *grid)); + +/* + * Glyph grid information functions. + */ +extern void bdf_grid_image __((bdf_glyph_grid_t *grid, bdf_bitmap_t *image)); +extern void bdf_grid_origin __((bdf_glyph_grid_t *grid, short *x, short *y)); +extern bdf_glyph_t *bdf_grid_glyph __((bdf_glyph_grid_t *grid)); + +/* + * Glyph grid editing functions. + */ +extern int bdf_grid_enlarge __((bdf_glyph_grid_t *grid, unsigned short width, + unsigned short height)); +extern int bdf_grid_resize __((bdf_glyph_grid_t *grid, + bdf_metrics_t *metrics)); +extern int bdf_grid_crop __((bdf_glyph_grid_t *grid, int grid_modified)); + +extern int bdf_grid_set_pixel __((bdf_glyph_grid_t *grid, short x, short y, + int val)); +extern int bdf_grid_clear_pixel __((bdf_glyph_grid_t *grid, short x, short y)); +extern int bdf_grid_invert_pixel __((bdf_glyph_grid_t *grid, + short x, short y, int val)); +extern int bdf_grid_shift __((bdf_glyph_grid_t *grid, short xcount, + short ycount)); +extern int bdf_grid_flip __((bdf_glyph_grid_t *grid, short dir)); +extern int bdf_grid_rotate __((bdf_glyph_grid_t *grid, short degrees, + int *resize)); +extern int bdf_grid_shear __((bdf_glyph_grid_t *grid, short degrees, + int *resize)); +extern int bdf_grid_embolden __((bdf_glyph_grid_t *grid)); + +/* + * Glyph grid selection functions. + */ +extern int bdf_has_selection __((bdf_glyph_grid_t *grid, short *x, short *y, + short *width, short *height)); +extern void bdf_set_selection __((bdf_glyph_grid_t *grid, short x, short y, + short width, short height)); +extern void bdf_lose_selection __((bdf_glyph_grid_t *grid)); +extern void bdf_detach_selection __((bdf_glyph_grid_t *grid)); +extern void bdf_attach_selection __((bdf_glyph_grid_t *grid)); +extern void bdf_delete_selection __((bdf_glyph_grid_t *grid)); +extern int bdf_in_selection __((bdf_glyph_grid_t *grid, short x, short y, + short *set)); +extern void bdf_add_selection __((bdf_glyph_grid_t *grid, bdf_bitmap_t *sel)); + +/* + * Glyph grid misc functions. + */ +extern int bdf_grid_color_at __((bdf_glyph_grid_t *grid, short x, short y)); + +/* + * Graphical transformation functions. + */ +extern int bdf_translate_glyphs __((bdf_font_t *font, short dx, short dy, + long start, long end, + bdf_callback_t callback, void *data, + int unencoded)); + +extern int bdf_rotate_glyphs __((bdf_font_t *font, short degrees, + long start, long end, + bdf_callback_t callback, void *data, + int unencoded)); + +extern int bdf_shear_glyphs __((bdf_font_t *font, short degrees, + long start, long end, + bdf_callback_t callback, void *data, + int unencoded)); + +extern int bdf_embolden_glyphs __((bdf_font_t *font, long start, long end, + bdf_callback_t callback, void *data, + int unencoded, int *resize)); + +extern int bdf_little_endian __((void)); + +#undef __ + +#ifdef __cplusplus +} +#endif + +#endif /* _h_bdf */ diff --git a/engines/sci/tools/bdfP.h b/engines/sci/tools/bdfP.h new file mode 100644 index 0000000000..9364035637 --- /dev/null +++ b/engines/sci/tools/bdfP.h @@ -0,0 +1,142 @@ +/* + * Copyright 2001 Computing Research Labs, New Mexico State University + * + * 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. + */ +#ifndef _h_bdfP +#define _h_bdfP + +/* + * $Id: bdfP.h 1284 2004-04-02 07:42:44Z jameson $ + */ + +#include "bdf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * A macro for prototypes. + */ +#undef __ +#ifdef __STDC__ +#define __(x) x +#else +#define __(x) () +#endif + +#ifndef MYABS +#define MYABS(xx) ((xx) < 0 ? -(xx) : (xx)) +#endif + +/* + * Macros and structures used for undo operations in the font. + */ +#define _UNDO_REPLACE_GLYPHS 1 +#define _UNDO_INSERT_GLYPHS 2 +#define _UNDO_MERGE_GLYPHS 3 + +/* + * This structure is for undo operations of replacing and merging glyphs + * in the font. + */ +typedef struct { + bdf_bbx_t b; + bdf_glyphlist_t g; +} _bdf_undo1_t; + +/* + * This structure is for undo operations of inserting glyphs. + */ +typedef struct { + bdf_bbx_t b; + long start; + long end; +} _bdf_undo2_t; + +/* + * This is the final undo structure used to store undo information with the + * font. + */ +typedef struct _bdf_undo_struct { + int type; + union { + _bdf_undo1_t one; + _bdf_undo2_t two; + } field; +} bdf_undo_struct_t; + +/* + * Tables for rotation and shearing. + */ +extern double _bdf_cos_tbl[]; +extern double _bdf_sin_tbl[]; +extern double _bdf_tan_tbl[]; + +/* + * Arrays of masks for test with different bits per pixel. + */ +extern unsigned char onebpp[]; +extern unsigned char twobpp[]; +extern unsigned char fourbpp[]; + +/* + * Simple routine for determining the ceiling. + */ +extern short _bdf_ceiling __((double v)); + +extern unsigned char *_bdf_strdup __((unsigned char *s, unsigned long len)); +extern void _bdf_memmove __((char *dest, char *src, unsigned long bytes)); + +extern short _bdf_atos __((char *s, char **end, int base)); +extern long _bdf_atol __((char *s, char **end, int base)); +extern unsigned long _bdf_atoul __((char *s, char **end, int base)); + +/* + * Macros to test/set the modified status of a glyph. + */ +#define _bdf_glyph_modified(map, e) ((map)[(e) >> 5] & (1 << ((e) & 31))) +#define _bdf_set_glyph_modified(map, e) (map)[(e) >> 5] |= (1 << ((e) & 31)) +#define _bdf_clear_glyph_modified(map, e) (map)[(e) >> 5] &= ~(1 << ((e) & 31)) + +/* + * Function to add a message to the font. + */ +extern void _bdf_add_acmsg __((bdf_font_t *font, char *msg, + unsigned long len)); + +/* + * Function to add a comment to the font. + */ +extern void _bdf_add_comment __((bdf_font_t *font, char *comment, + unsigned long len)); + +/* + * Function to do glyph name table cleanup when exiting. + */ +extern void _bdf_glyph_name_cleanup __((void)); + +#undef __ + +#ifdef __cplusplus +} +#endif + +#endif /* _h_bdfP */ diff --git a/engines/sci/tools/bdfgname.c b/engines/sci/tools/bdfgname.c new file mode 100644 index 0000000000..200202562e --- /dev/null +++ b/engines/sci/tools/bdfgname.c @@ -0,0 +1,431 @@ +/* + * Copyright 2001 Computing Research Labs, New Mexico State University + * + * 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. + */ +#ifndef lint +#ifdef __GNUC__ +static char rcsid[] __attribute__ ((unused)) = "$Id: bdfgname.c 1284 2004-04-02 07:42:44Z jameson $"; +#else +static char rcsid[] = "$Id: bdfgname.c 1284 2004-04-02 07:42:44Z jameson $"; +#endif +#endif + +#include "bdfP.h" + +typedef struct { + long code; + long start; + long end; + long pad; +} _bdf_adobe_name_t; + +static _bdf_adobe_name_t *adobe_names; +static unsigned long adobe_names_size; +static unsigned long adobe_names_used; + +/* + * Provide a maximum length for glyph names just to make things clearer. + */ +#define MAX_GLYPH_NAME_LEN 127 + +static int +#ifdef __STDC__ +getline(FILE *in, char *buf, int limit) +#else +getline(in, buf, limit) +FILE *in; +char *buf; +int limit; +#endif +{ + int c, i; + + c = EOF; + + for (i = 0; i < limit - 1; i++) { + if ((c = getc(in)) == EOF || (c == '\n' || c == '\r')) + break; + buf[i] = c; + } + buf[i] = 0; + + /* + * Discard the rest of the line which did not fit into the buffer. + */ + while (c != EOF && c != '\n' && c != '\r') + c = getc(in); + + if (c == '\r') { + /* + * Check for a trailing newline. + */ + c = getc(in); + if (c != '\n') + ungetc(c, in); + } + + return i; +} + +static long +#ifdef __STDC__ +_bdf_find_name(long code, char *name, FILE *in) +#else +_bdf_find_name(code, name, in) +long code; +char *name; +FILE *in; +#endif +{ + long c, i, pos; + char *sp, buf[256]; + + while (!feof(in)) { + pos = ftell(in); + (void) getline(in, buf, 256); + while (!feof(in) && (buf[0] == 0 || buf[0] == '#')) { + buf[0] = 0; + pos = ftell(in); + (void) getline(in, buf, 256); + } + + if (buf[0] == 0) + return -1; + + c = _bdf_atol(buf, 0, 16); + + if (c > code) { + /* + * Restore the last position read in case the code is not in the + * file and the current code is greater than the expected code. + */ + fseek(in, pos, 0L); + return -1; + } + + if (c == code) { + for (sp = buf; *sp != ';'; sp++) ; + sp++; + for (i = 0; *sp != ';' && i < MAX_GLYPH_NAME_LEN; sp++, i++) + name[i] = *sp; + name[i] = 0; + return i; + } + } + return -1; +} + +static int +#ifdef __STDC__ +by_encoding(const void *a, const void *b) +#else +by_encoding(a, b) +char *a, *b; +#endif +{ + _bdf_adobe_name_t *c1, *c2; + + c1 = (_bdf_adobe_name_t *) a; + c2 = (_bdf_adobe_name_t *) b; + if (c1->code < c2->code) + return -1; + else if (c1->code > c2->code) + return 1; + return 0; +} + +static void +#ifdef __STDC__ +_bdf_load_adobe_names(FILE *in) +#else +_bdf_load_adobe_names(in) +FILE *in; +#endif +{ + long c, pos; + char *sp, buf[256]; + + /* + * Go back to the beginning of the file to look for the code because the + * codes are not in order in the current Adobe Glyph Name list file. + */ + fseek(in, 0, 0); + + while (!feof(in)) { + pos = ftell(in); + (void) getline(in, buf, 256); + while (!feof(in) && (buf[0] == 0 || buf[0] == '#')) { + buf[0] = 0; + pos = ftell(in); + (void) getline(in, buf, 256); + } + + c = _bdf_atol(buf, 0, 16); + + /* + * Ignore the Adobe-specific names in the Private Use Area. + */ + if (c >= 0xe000 && c <= 0xf8ff) + continue; + + if (adobe_names_used == adobe_names_size) { + if (adobe_names_size == 0) + adobe_names = (_bdf_adobe_name_t *) + malloc(sizeof(_bdf_adobe_name_t) << 9); + else + adobe_names = (_bdf_adobe_name_t *) + realloc((char *) adobe_names, + sizeof(_bdf_adobe_name_t) * + (adobe_names_size + 512)); + (void) memset((char *) (adobe_names + adobe_names_size), 0, + sizeof(_bdf_adobe_name_t) << 9); + adobe_names_size += 512; + } + + adobe_names[adobe_names_used].code = c; + for (sp = buf; *sp != ';'; sp++) ; + sp++; + adobe_names[adobe_names_used].start = pos + (sp - buf); + for (; *sp != ';'; sp++) ; + adobe_names[adobe_names_used].end = pos + (sp - buf); + adobe_names_used++; + } + + /* + * Sort the results by code. + */ + qsort((char *) adobe_names, adobe_names_used, sizeof(_bdf_adobe_name_t), + by_encoding); +} + +static long +#ifdef __STDC__ +_bdf_find_adobe_name(long code, char *name, FILE *in) +#else +_bdf_find_adobe_name(code, name, in) +long code; +char *name; +FILE *in; +#endif +{ + long len; + int l, r, m; + + if (code < 0x20 || (code >= 0x7f && code <= 0x9f) || + code == 0xfffe || code == 0xffff) { + sprintf(name, "char%lu", code); + return (long) strlen(name); + } + + if (code >= 0xe000 && code <= 0xf8ff) { + sprintf(name, "uni%04lX", code & 0xffff); + return (long) strlen(name); + } + + if (adobe_names_size == 0) + _bdf_load_adobe_names(in); + + l = 0; + r = adobe_names_used - 1; + while (l <= r) { + m = (l + r) >> 1; + if (adobe_names[m].code < code) + l = m + 1; + else if (adobe_names[m].code > code) + r = m - 1; + else { + fseek(in, adobe_names[m].start, 0); + len = adobe_names[m].end - adobe_names[m].start; + if (len > MAX_GLYPH_NAME_LEN) + len = MAX_GLYPH_NAME_LEN; + len = (long) fread(name, sizeof(char), len, in); + name[len] = 0; + return len; + } + } + + sprintf(name, "uni%04lX", code & 0xffff); + return (long) strlen(name); +} + +static int +#ifdef __STDC__ +_bdf_set_glyph_names(FILE *in, bdf_font_t *font, bdf_callback_t callback, + int adobe) +#else +_bdf_set_glyph_names(in, font, callback, adobe) +FILE *in; +bdf_font_t *font; +bdf_callback_t callback; +int adobe; +#endif +{ + int changed; + long i, size, len; + bdf_glyph_t *gp; + bdf_callback_struct_t cb; + char name[MAX_GLYPH_NAME_LEN + 1]; + + if (callback != 0) { + cb.reason = BDF_GLYPH_NAME_START; + cb.current = 0; + cb.total = font->glyphs_used; + (*callback)(&cb, 0); + } + for (changed = 0, i = 0, gp = font->glyphs; i < font->glyphs_used; + i++, gp++) { + size = (adobe) ? + _bdf_find_adobe_name(gp->encoding, name, in) : + _bdf_find_name(gp->encoding, name, in); + if (size < 0) + continue; + + len = (gp->name) ? strlen(gp->name) : 0; + if (len == 0) { + gp->name = (char *) _bdf_strdup((unsigned char *) name, size + 1); + changed = 1; + } else if (size != len || strcmp(gp->name, name) != 0) { + /* + * Simply resize existing storage so lots of memory allocations + * are not needed. + */ + if (size > len) + gp->name = (char *) realloc(gp->name, size + 1); + (void) strcpy(gp->name, name); + changed = 1; + } + + if (callback != 0) { + cb.reason = BDF_GLYPH_NAME; + cb.current = i; + (*callback)(&cb, 0); + } + } + + if (callback != 0) { + cb.reason = BDF_GLYPH_NAME; + cb.current = cb.total; + (*callback)(&cb, 0); + } + + return changed; +} + +int +#ifdef __STDC__ +bdf_set_unicode_glyph_names(FILE *in, bdf_font_t *font, + bdf_callback_t callback) +#else +bdf_set_unicode_glyph_names(in, font, callback) +FILE *in; +bdf_font_t *font; +bdf_callback_t callback; +#endif +{ + return _bdf_set_glyph_names(in, font, callback, 0); +} + +int +#ifdef __STDC__ +bdf_set_adobe_glyph_names(FILE *in, bdf_font_t *font, bdf_callback_t callback) +#else +bdf_set_adobe_glyph_names(in, font, callback) +FILE *in; +bdf_font_t *font; +bdf_callback_t callback; +#endif +{ + return _bdf_set_glyph_names(in, font, callback, 1); +} + +int +#ifdef __STDC__ +bdf_set_glyph_code_names(int prefix, bdf_font_t *font, bdf_callback_t callback) +#else +bdf_set_glyph_code_names(prefix, font, callback) +int prefix; +bdf_font_t *font; +bdf_callback_t callback; +#endif +{ + int changed; + long i, size, len; + bdf_glyph_t *gp; + bdf_callback_struct_t cb; + char name[128]; + + if (callback != 0) { + cb.reason = BDF_GLYPH_NAME_START; + cb.current = 0; + cb.total = font->glyphs_used; + (*callback)(&cb, 0); + } + for (changed = 0, i = 0, gp = font->glyphs; i < font->glyphs_used; + i++, gp++) { + switch (prefix) { + case 'u': sprintf(name, "uni%04lX", gp->encoding & 0xffff); break; + case 'x': sprintf(name, "0x%04lX", gp->encoding & 0xffff); break; + case '+': sprintf(name, "U+%04lX", gp->encoding & 0xffff); break; + case '\\': sprintf(name, "\\u%04lX", gp->encoding & 0xffff); break; + } + size = 6; + + len = (gp->name) ? strlen(gp->name) : 0; + if (len == 0) { + gp->name = (char *) _bdf_strdup((unsigned char *) name, size + 1); + changed = 1; + } else if (size != len || strcmp(gp->name, name) != 0) { + /* + * Simply resize existing storage so lots of memory allocations + * are not needed. + */ + if (size > len) + gp->name = (char *) realloc(gp->name, size + 1); + (void) strcpy(gp->name, name); + changed = 1; + } + + if (callback != 0) { + cb.reason = BDF_GLYPH_NAME; + cb.current = i; + (*callback)(&cb, 0); + } + } + + if (callback != 0) { + cb.reason = BDF_GLYPH_NAME; + cb.current = cb.total; + (*callback)(&cb, 0); + } + + return changed; +} + +void +#ifdef __STDC__ +_bdf_glyph_name_cleanup(void) +#else +_bdf_glyph_name_cleanup() +#endif +{ + if (adobe_names_size > 0) + free((char *) adobe_names); + adobe_names_size = adobe_names_used = 0; +} diff --git a/engines/sci/tools/bdfgrid.c b/engines/sci/tools/bdfgrid.c new file mode 100644 index 0000000000..4bbd022d1e --- /dev/null +++ b/engines/sci/tools/bdfgrid.c @@ -0,0 +1,3361 @@ +/* + * Copyright 2001 Computing Research Labs, New Mexico State University + * + * 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. + */ +#ifndef lint +#ifdef __GNUC__ +static char rcsid[] __attribute__ ((unused)) = "$Id: bdfgrid.c 1284 2004-04-02 07:42:44Z jameson $"; +#else +static char rcsid[] = "$Id: bdfgrid.c 1284 2004-04-02 07:42:44Z jameson $"; +#endif +#endif + +#include "bdfP.h" + +#ifndef MYABS +#define MYABS(n) ((n) < 0 ? -(n) : (n)) +#endif + +#undef MAX +#define MAX(h, i) ((h) > (i) ? (h) : (i)) + +#undef MIN +#define MIN(l, o) ((l) < (o) ? (l) : (o)) + +double _bdf_cos_tbl[360] = { + 0.000000, 0.999848, 0.999391, 0.998630, 0.997564, 0.996195, + 0.994522, 0.992546, 0.990268, 0.987688, 0.984808, 0.981627, + 0.978148, 0.974370, 0.970296, 0.965926, 0.961262, 0.956305, + 0.951057, 0.945519, 0.939693, 0.933580, 0.927184, 0.920505, + 0.913545, 0.906308, 0.898794, 0.891007, 0.882948, 0.874620, + 0.866025, 0.857167, 0.848048, 0.838671, 0.829038, 0.819152, + 0.809017, 0.798636, 0.788011, 0.777146, 0.766044, 0.754710, + 0.743145, 0.731354, 0.719340, 0.707107, 0.694658, 0.681998, + 0.669131, 0.656059, 0.642788, 0.629320, 0.615661, 0.601815, + 0.587785, 0.573576, 0.559193, 0.544639, 0.529919, 0.515038, + 0.500000, 0.484810, 0.469472, 0.453990, 0.438371, 0.422618, + 0.406737, 0.390731, 0.374607, 0.358368, 0.342020, 0.325568, + 0.309017, 0.292372, 0.275637, 0.258819, 0.241922, 0.224951, + 0.207912, 0.190809, 0.173648, 0.156434, 0.139173, 0.121869, + 0.104528, 0.087156, 0.069756, 0.052336, 0.034899, 0.017452, + 0.000000, -0.017452, -0.034899, -0.052336, -0.069756, -0.087156, + -0.104528, -0.121869, -0.139173, -0.156434, -0.173648, -0.190809, + -0.207912, -0.224951, -0.241922, -0.258819, -0.275637, -0.292372, + -0.309017, -0.325568, -0.342020, -0.358368, -0.374607, -0.390731, + -0.406737, -0.422618, -0.438371, -0.453990, -0.469472, -0.484810, + -0.500000, -0.515038, -0.529919, -0.544639, -0.559193, -0.573576, + -0.587785, -0.601815, -0.615661, -0.629320, -0.642788, -0.656059, + -0.669131, -0.681998, -0.694658, -0.707107, -0.719340, -0.731354, + -0.743145, -0.754710, -0.766044, -0.777146, -0.788011, -0.798636, + -0.809017, -0.819152, -0.829038, -0.838671, -0.848048, -0.857167, + -0.866025, -0.874620, -0.882948, -0.891007, -0.898794, -0.906308, + -0.913545, -0.920505, -0.927184, -0.933580, -0.939693, -0.945519, + -0.951057, -0.956305, -0.961262, -0.965926, -0.970296, -0.974370, + -0.978148, -0.981627, -0.984808, -0.987688, -0.990268, -0.992546, + -0.994522, -0.996195, -0.997564, -0.998630, -0.999391, -0.999848, + -1.000000, -0.999848, -0.999391, -0.998630, -0.997564, -0.996195, + -0.994522, -0.992546, -0.990268, -0.987688, -0.984808, -0.981627, + -0.978148, -0.974370, -0.970296, -0.965926, -0.961262, -0.956305, + -0.951057, -0.945519, -0.939693, -0.933580, -0.927184, -0.920505, + -0.913545, -0.906308, -0.898794, -0.891007, -0.882948, -0.874620, + -0.866025, -0.857167, -0.848048, -0.838671, -0.829038, -0.819152, + -0.809017, -0.798636, -0.788011, -0.777146, -0.766044, -0.754710, + -0.743145, -0.731354, -0.719340, -0.707107, -0.694658, -0.681998, + -0.669131, -0.656059, -0.642788, -0.629320, -0.615661, -0.601815, + -0.587785, -0.573576, -0.559193, -0.544639, -0.529919, -0.515038, + -0.500000, -0.484810, -0.469472, -0.453990, -0.438371, -0.422618, + -0.406737, -0.390731, -0.374607, -0.358368, -0.342020, -0.325568, + -0.309017, -0.292372, -0.275637, -0.258819, -0.241922, -0.224951, + -0.207912, -0.190809, -0.173648, -0.156434, -0.139173, -0.121869, + -0.104528, -0.087156, -0.069756, -0.052336, -0.034899, -0.017452, + -0.000000, 0.017452, 0.034899, 0.052336, 0.069756, 0.087156, + 0.104528, 0.121869, 0.139173, 0.156434, 0.173648, 0.190809, + 0.207912, 0.224951, 0.241922, 0.258819, 0.275637, 0.292372, + 0.309017, 0.325568, 0.342020, 0.358368, 0.374607, 0.390731, + 0.406737, 0.422618, 0.438371, 0.453990, 0.469472, 0.484810, + 0.500000, 0.515038, 0.529919, 0.544639, 0.559193, 0.573576, + 0.587785, 0.601815, 0.615661, 0.629320, 0.642788, 0.656059, + 0.669131, 0.681998, 0.694658, 0.707107, 0.719340, 0.731354, + 0.743145, 0.754710, 0.766044, 0.777146, 0.788011, 0.798636, + 0.809017, 0.819152, 0.829038, 0.838671, 0.848048, 0.857167, + 0.866025, 0.874620, 0.882948, 0.891007, 0.898794, 0.906308, + 0.913545, 0.920505, 0.927184, 0.933580, 0.939693, 0.945519, + 0.951057, 0.956305, 0.961262, 0.965926, 0.970296, 0.974370, + 0.978148, 0.981627, 0.984808, 0.987688, 0.990268, 0.992546, + 0.994522, 0.996195, 0.997564, 0.998630, 0.999391, 0.999848, +}; + +double _bdf_sin_tbl[360] = { + 0.000000, 0.017452, 0.034899, 0.052336, 0.069756, 0.087156, + 0.104528, 0.121869, 0.139173, 0.156434, 0.173648, 0.190809, + 0.207912, 0.224951, 0.241922, 0.258819, 0.275637, 0.292372, + 0.309017, 0.325568, 0.342020, 0.358368, 0.374607, 0.390731, + 0.406737, 0.422618, 0.438371, 0.453990, 0.469472, 0.484810, + 0.500000, 0.515038, 0.529919, 0.544639, 0.559193, 0.573576, + 0.587785, 0.601815, 0.615661, 0.629320, 0.642788, 0.656059, + 0.669131, 0.681998, 0.694658, 0.707107, 0.719340, 0.731354, + 0.743145, 0.754710, 0.766044, 0.777146, 0.788011, 0.798636, + 0.809017, 0.819152, 0.829038, 0.838671, 0.848048, 0.857167, + 0.866025, 0.874620, 0.882948, 0.891007, 0.898794, 0.906308, + 0.913545, 0.920505, 0.927184, 0.933580, 0.939693, 0.945519, + 0.951057, 0.956305, 0.961262, 0.965926, 0.970296, 0.974370, + 0.978148, 0.981627, 0.984808, 0.987688, 0.990268, 0.992546, + 0.994522, 0.996195, 0.997564, 0.998630, 0.999391, 0.999848, + 1.000000, 0.999848, 0.999391, 0.998630, 0.997564, 0.996195, + 0.994522, 0.992546, 0.990268, 0.987688, 0.984808, 0.981627, + 0.978148, 0.974370, 0.970296, 0.965926, 0.961262, 0.956305, + 0.951057, 0.945519, 0.939693, 0.933580, 0.927184, 0.920505, + 0.913545, 0.906308, 0.898794, 0.891007, 0.882948, 0.874620, + 0.866025, 0.857167, 0.848048, 0.838671, 0.829038, 0.819152, + 0.809017, 0.798636, 0.788011, 0.777146, 0.766044, 0.754710, + 0.743145, 0.731354, 0.719340, 0.707107, 0.694658, 0.681998, + 0.669131, 0.656059, 0.642788, 0.629320, 0.615661, 0.601815, + 0.587785, 0.573576, 0.559193, 0.544639, 0.529919, 0.515038, + 0.500000, 0.484810, 0.469472, 0.453990, 0.438371, 0.422618, + 0.406737, 0.390731, 0.374607, 0.358368, 0.342020, 0.325568, + 0.309017, 0.292372, 0.275637, 0.258819, 0.241922, 0.224951, + 0.207912, 0.190809, 0.173648, 0.156434, 0.139173, 0.121869, + 0.104528, 0.087156, 0.069756, 0.052336, 0.034899, 0.017452, + 0.000000, -0.017452, -0.034899, -0.052336, -0.069756, -0.087156, + -0.104528, -0.121869, -0.139173, -0.156434, -0.173648, -0.190809, + -0.207912, -0.224951, -0.241922, -0.258819, -0.275637, -0.292372, + -0.309017, -0.325568, -0.342020, -0.358368, -0.374607, -0.390731, + -0.406737, -0.422618, -0.438371, -0.453990, -0.469472, -0.484810, + -0.500000, -0.515038, -0.529919, -0.544639, -0.559193, -0.573576, + -0.587785, -0.601815, -0.615661, -0.629320, -0.642788, -0.656059, + -0.669131, -0.681998, -0.694658, -0.707107, -0.719340, -0.731354, + -0.743145, -0.754710, -0.766044, -0.777146, -0.788011, -0.798636, + -0.809017, -0.819152, -0.829038, -0.838671, -0.848048, -0.857167, + -0.866025, -0.874620, -0.882948, -0.891007, -0.898794, -0.906308, + -0.913545, -0.920505, -0.927184, -0.933580, -0.939693, -0.945519, + -0.951057, -0.956305, -0.961262, -0.965926, -0.970296, -0.974370, + -0.978148, -0.981627, -0.984808, -0.987688, -0.990268, -0.992546, + -0.994522, -0.996195, -0.997564, -0.998630, -0.999391, -0.999848, + -1.000000, -0.999848, -0.999391, -0.998630, -0.997564, -0.996195, + -0.994522, -0.992546, -0.990268, -0.987688, -0.984808, -0.981627, + -0.978148, -0.974370, -0.970296, -0.965926, -0.961262, -0.956305, + -0.951057, -0.945519, -0.939693, -0.933580, -0.927184, -0.920505, + -0.913545, -0.906308, -0.898794, -0.891007, -0.882948, -0.874620, + -0.866025, -0.857167, -0.848048, -0.838671, -0.829038, -0.819152, + -0.809017, -0.798636, -0.788011, -0.777146, -0.766044, -0.754710, + -0.743145, -0.731354, -0.719340, -0.707107, -0.694658, -0.681998, + -0.669131, -0.656059, -0.642788, -0.629320, -0.615661, -0.601815, + -0.587785, -0.573576, -0.559193, -0.544639, -0.529919, -0.515038, + -0.500000, -0.484810, -0.469472, -0.453990, -0.438371, -0.422618, + -0.406737, -0.390731, -0.374607, -0.358368, -0.342020, -0.325568, + -0.309017, -0.292372, -0.275637, -0.258819, -0.241922, -0.224951, + -0.207912, -0.190809, -0.173648, -0.156434, -0.139173, -0.121869, + -0.104528, -0.087156, -0.069756, -0.052336, -0.034899, -0.017452, +}; + +double _bdf_tan_tbl[90] = { + 0.000000, 0.017455, 0.034921, 0.052408, 0.069927, 0.087489, + 0.105104, 0.122785, 0.140541, 0.158384, 0.176327, 0.194380, + 0.212557, 0.230868, 0.249328, 0.267949, 0.286745, 0.305731, + 0.324920, 0.344328, 0.363970, 0.383864, 0.404026, 0.424475, + 0.445229, 0.466308, 0.487733, 0.509525, 0.531709, 0.554309, + 0.577350, 0.600861, 0.624869, 0.649408, 0.674509, 0.700208, + 0.726543, 0.753554, 0.781286, 0.809784, 0.839100, 0.869287, + 0.900404, 0.932515, 0.965689, 1.000000, 1.035530, 1.072369, + 1.110613, 1.150368, 1.191754, 1.234897, 1.279942, 1.327045, + 1.376382, 1.428148, 1.482561, 1.539865, 1.600335, 1.664279, + 1.732051, 1.804048, 1.880726, 1.962611, 2.050304, 2.144507, + 2.246037, 2.355852, 2.475087, 2.605089, 2.747477, 2.904211, + 3.077684, 3.270853, 3.487414, 3.732051, 4.010781, 4.331476, + 4.704630, 5.144554, 5.671282, 6.313752, 7.115370, 8.144346, + 9.514364, 11.430052, 14.300666, 19.081137, 28.636253, 57.289962, +}; + +/* + * Determine the actual ink bounds. + */ +static int +#ifdef __STDC__ +_bdf_grid_ink_bounds(bdf_glyph_grid_t *grid, short *x, short *y, + short *width, short *height) +#else +_bdf_grid_ink_bounds(grid, x, y, width, height) +bdf_glyph_grid_t *grid; +short *x, *y, *width, *height; +#endif +{ + short bx, by, bwd, bht, minx, maxx, miny, maxy, dx, dy; + unsigned short bpr, ink, sel, col; + unsigned char *bmap, *masks; + + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + if (grid->sel.width != 0 && grid->sel.height != 0) { + sel = 1; + bx = by = 0; + bwd = grid->sel.width; + bht = grid->sel.height; + bmap = grid->sel.bitmap; + } else { + sel = 0; + bx = grid->glyph_x; + by = grid->glyph_y; + bwd = grid->glyph_bbx.width; + bht = grid->glyph_bbx.height; + bmap = grid->bitmap; + } + maxx = maxy = 0; + minx = bx + bwd; + miny = by + bht; + + bpr = ((bwd * grid->bpp) + 7) >> 3; + ink = 0; + + bwd += bx; + bht += by; + for (dy = by; dy < bht; dy++) { + for (col = bx * grid->bpp, dx = bx; dx < bwd; dx++, col += grid->bpp) { + if (bmap[(dy * bpr) + (col >> 3)] & masks[(col & 7) / grid->bpp]) { + ink = 1; + minx = MIN(minx, dx); + miny = MIN(miny, dy); + maxx = MAX(maxx, dx); + maxy = MAX(maxy, dy); + } + } + } + + *x = minx + ((sel) ? grid->sel.x : 0); + *y = miny + ((sel) ? grid->sel.y : 0); + if (ink == 0) + *width = *height = 0; + else { + *width = (maxx - minx) + 1; + *height = (maxy - miny) + 1; + } + return ink; +} + +/************************************************************************** + * + * Glyph grid create and destroy functions. + * + **************************************************************************/ + +/* + * Make a glyph grid with the glyph bitmap set in the bitmap. + */ +bdf_glyph_grid_t * +#ifdef __STDC__ +bdf_make_glyph_grid(bdf_font_t *font, long code, int unencoded) +#else +bdf_make_glyph_grid(font, code, unencoded) +bdf_font_t *font; +long code; +int unencoded; +#endif +{ + unsigned short si, di, col, colx, byte; + short ht, as, ds, gsize, bpr, x, y, nx, ny; + long l, r, m; + bdf_glyph_grid_t *gr; + bdf_glyph_t *gl, *glp; + bdf_property_t *p; + unsigned char *masks; + char name[24]; + + if (font == 0) + return 0; + + /* + * Allocate the grid and initialize it. + */ + gr = (bdf_glyph_grid_t *) malloc(sizeof(bdf_glyph_grid_t)); + (void) memset((char *) gr, 0, sizeof(bdf_glyph_grid_t)); + + /* + * Set the encoding and the unencoded flag. + */ + gr->bpp = font->bpp; + gr->encoding = code; + gr->unencoded = unencoded; + + /* + * Set the glyph grid spacing. + */ + gr->spacing = font->spacing; + + /* + * Set the point size and resolutions. + */ + gr->point_size = font->point_size; + gr->resolution_x = font->resolution_x; + gr->resolution_y = font->resolution_y; + + /* + * Set the CAP_HEIGHT and X_HEIGHT if they exist in the font. + */ + if ((p = bdf_get_font_property(font, "CAP_HEIGHT")) != 0) + gr->cap_height = (short) p->value.int32; + if ((p = bdf_get_font_property(font, "X_HEIGHT")) != 0) + gr->x_height = (short) p->value.int32; + + masks = 0; + switch (gr->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + /* + * Copy the font bounding box into the grid. + */ + (void) memcpy((char *) &gr->font_bbx, (char *) &font->bbx, + sizeof(bdf_bbx_t)); + + if (unencoded) { + gl = font->unencoded; + r = font->unencoded_used; + } else { + gl = font->glyphs; + r = font->glyphs_used; + } + + /* + * Locate the specified glyph using a simple binary search. + */ + glp = 0; + if (r > 0) { + for (l = 0; r >= l; ) { + m = (l + r) >> 1; + glp = gl + m; + if (glp->encoding == code) + break; + if (glp->encoding > code) + r = m - 1; + else if (glp->encoding < code) + l = m + 1; + glp = 0; + } + } + + ht = gr->font_bbx.height; + as = gr->font_bbx.ascent; + ds = gr->font_bbx.descent; + + /* + * 1. Determine width and height needed from the largest of the + * width or height. + */ + gr->grid_width = gr->grid_height = + MAX(gr->font_bbx.width, gr->font_bbx.height); + + /* + * 2. Make sure the grid is at least a square of the largest of the width + * or height of the glyph itself to allow room for transformations. + */ + if (glp != 0) { + /* + * Set the glyph name and other metrics. + */ + if (glp->name) { + gr->name = (char *) malloc(strlen(glp->name) + 1); + (void) memcpy(gr->name, glp->name, strlen(glp->name) + 1); + } else { + sprintf(name, "char%ld", code); + gr->name = (char *) malloc(strlen(name) + 1); + (void) memcpy(gr->name, name, strlen(name) + 1); + } + gr->dwidth = glp->dwidth; + + /* + * Copy the glyph bounding box into the grid. + */ + (void) memcpy((char *) &gr->glyph_bbx, (char *) &glp->bbx, + sizeof(bdf_bbx_t)); + + if (glp->bbx.height < glp->bbx.ascent + glp->bbx.descent) + gsize = glp->bbx.ascent + glp->bbx.descent; + else + gsize = glp->bbx.height; + + /* + * Figure the maximum of the glyph width and height. + */ + gsize = MAX(gr->glyph_bbx.width, gsize); + + /* + * If either the grid width or grid height is less than the + * grid size just determined, then adjust them to the new grid size. + */ + gr->grid_width = MAX(gr->grid_width, gsize); + gr->grid_height = MAX(gr->grid_height, gsize); + } else { + /* + * The glyph doesn't exist, so make up a name for it. + */ + if (unencoded) + sprintf(name, "unencoded%ld", code); + else + sprintf(name, "char%ld", code); + gr->name = (char *) malloc(strlen(name) + 1); + (void) memcpy(gr->name, name, strlen(name) + 1); + } + + /* + * If the font has character-cell or mono spacing, make sure the grid + * device width is set to the width stored in the font. + */ + if (gr->spacing != BDF_PROPORTIONAL) + gr->dwidth = font->monowidth; + + /* + * Determine the vertical origin based on the font bounding box. + */ + if (ht >= as + ds) + gr->base_y = (((gr->grid_height >> 1) - (ht >> 1)) + ht) - ds; + else + gr->base_y = ((gr->grid_height >> 1) - ((as + ds) >> 1)) + as; + + /* + * The final adjust is to check to see if the glyph positioned relative to + * the baseline would cause the grid to change size. This sometimes + * happens in fonts that have incorrect metrics. + */ + if (gr->base_y + gr->glyph_bbx.descent > gr->grid_height) { + gsize = gr->base_y + gr->glyph_bbx.descent; + gr->grid_width = MAX(gsize, gr->grid_width); + gr->grid_height = MAX(gsize, gr->grid_height); + } + + /* + * Determine the horizontal origin based on the font bounding box and + * centered within the grid. + */ + gr->base_x = (gr->grid_width >> 1) - (gr->font_bbx.width >> 1); + if (gr->font_bbx.x_offset < 0) + gr->base_x += MYABS(gr->font_bbx.x_offset); + + /* + * Allocate double the storage needed for the grid bitmap. The extra + * storage will be used for transformations. + */ + gr->bytes = ((((gr->grid_width * gr->bpp) + 7) >> 3) * + gr->grid_height) << 1; + gr->bitmap = (unsigned char *) malloc(gr->bytes); + (void) memset((char *) gr->bitmap, 0, gr->bytes); + + /* + * Initialize the top-left coordinates of the glyph to the baseline + * coordinates. + */ + gr->glyph_x = gr->base_x; + gr->glyph_y = gr->base_y; + + /* + * If the glyph was not found, simply return the empty grid. + */ + if (glp == 0) + return gr; + + /* + * Determine the top-left coordinates of the glyph with respect to the + * baseline coordinates. + */ + gr->glyph_x = nx = gr->base_x + gr->glyph_bbx.x_offset; + gr->glyph_y = ny = gr->base_y - gr->glyph_bbx.ascent; + + /* + * Now copy the glyph bitmap to the appropriate location in the + * grid. + */ + bpr = ((gr->glyph_bbx.width * gr->bpp) + 7) >> 3; + gsize = ((gr->grid_width * gr->bpp) + 7) >> 3; + for (y = 0; y < gr->glyph_bbx.height; y++, ny++) { + for (colx = nx * gr->bpp, col = x = 0; x < gr->glyph_bbx.width; + x++, col += gr->bpp, colx += gr->bpp) { + si = (col & 7) / gr->bpp; + byte = glp->bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + di = (colx & 7) / gr->bpp; + if (di < si) + byte <<= (si - di) * gr->bpp; + else if (di > si) + byte >>= (di - si) * gr->bpp; + gr->bitmap[(ny * gsize) + (colx >> 3)] |= byte; + } + } + } + + /* + * Always crop the glyph to the ink bounds before editing. + */ + bdf_grid_crop(gr, 0); + + /* + * Return the grid. + */ + return gr; +} + +void +#ifdef __STDC__ +bdf_free_glyph_grid(bdf_glyph_grid_t *grid) +#else +bdf_free_glyph_grid(grid) +bdf_glyph_grid_t *grid; +#endif +{ + if (grid == 0) + return; + + if (grid->name != 0) + free(grid->name); + if (grid->bytes > 0) + free((char *) grid->bitmap); + if (grid->sel.bytes > 0) + free((char *) grid->sel.bitmap); + free((char *) grid); +} + +/************************************************************************** + * + * Glyph grid resize functions. + * + **************************************************************************/ + +/* + * Enlarge the grid without affecting the font or glyph metrics. + */ +int +#ifdef __STDC__ +bdf_grid_enlarge(bdf_glyph_grid_t *grid, unsigned short width, + unsigned short height) +#else +bdf_grid_enlarge(grid, width, height) +bdf_glyph_grid_t *grid; +unsigned short width, height; +#endif +{ + unsigned short si, di, col, colx, byte; + short ht, wd, as, ds, x, y, nx, ny; + unsigned short gwd, ght, bytes, obpr, nbpr, gsize; + unsigned char *bitmap, *masks; + + if (grid == 0 || (width < grid->grid_width && height < grid->grid_height)) + return 0; + + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + ht = height; + as = grid->font_bbx.ascent; + ds = grid->font_bbx.descent; + + gwd = MAX(width, grid->grid_width); + ght = MAX(height, grid->grid_height); + gsize = MAX(gwd, ght); + + nbpr = ((gsize * grid->bpp) + 7) >> 3; + bytes = (nbpr * ght) << 1; + bitmap = (unsigned char *) malloc(bytes); + (void) memset((char *) bitmap, 0, bytes); + + /* + * Determine the new baseline. + */ + if (ht >= as + ds) + grid->base_y = (((ght >> 1) - (ht >> 1)) + ht) - ds; + else + grid->base_y = ((ght >> 1) - ((as + ds) >> 1)) + as; + + grid->base_x = (gwd >> 1) - (grid->font_bbx.width >> 1); + if (grid->font_bbx.x_offset < 0) + grid->base_x += MYABS(grid->font_bbx.x_offset); + + nx = grid->base_x + grid->glyph_bbx.x_offset; + ny = grid->base_y - grid->glyph_bbx.ascent; + + /* + * Now copy the bitmap into the new storage base on the new metrics + * values. + */ + obpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + wd = grid->glyph_x + grid->glyph_bbx.width; + ht = grid->glyph_y + grid->glyph_bbx.height; + for (y = grid->glyph_y; y < ht; y++, ny++) { + col = grid->glyph_x * grid->bpp; + colx = nx * grid->bpp; + for (x = grid->glyph_x; x < wd; + x++, col += grid->bpp, colx += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = grid->bitmap[(y * obpr) + (col >> 3)] & masks[si]; + if (byte) { + di = (colx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + bitmap[(ny * nbpr) + (colx >> 3)] |= byte; + } + } + } + + /* + * Adjust the glyph coordinates. + */ + grid->glyph_x = grid->base_x + grid->glyph_bbx.x_offset; + grid->glyph_y = grid->base_y - grid->glyph_bbx.ascent; + + /* + * Get rid of the old grid bitmap and replace it with the new one. + */ + free((char *) grid->bitmap); + grid->bytes = bytes; + grid->bitmap = bitmap; + + /* + * Update the new grid width and height. + */ + grid->grid_width = grid->grid_height = gsize; + + /* + * Always mark the grid as being modified on a resize. + */ + grid->modified = 1; + + return 1; +} + +/* + * Change the font bounding box values and resize the grid bitmap if + * necessary. + */ +int +#ifdef __STDC__ +bdf_grid_resize(bdf_glyph_grid_t *grid, bdf_metrics_t *metrics) +#else +bdf_grid_resize(grid, metrics) +bdf_glyph_grid_t *grid; +bdf_metrics_t *metrics; +#endif +{ + int changed; + unsigned short si, di, col, colx, byte; + short ht, wd, as, ds, x, y, nx, ny; + unsigned short gwd, ght, bytes, obpr, nbpr, gsize; + unsigned char *bitmap, *masks; + + changed = 0; + + if (grid == 0 || metrics == 0) + return changed; + + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + /* + * Create new grid bitmaps in preparation for the various metrics changing. + */ + if (metrics->width > grid->grid_width || + metrics->height > grid->grid_height) { + changed = 1; + + ht = metrics->height; + as = metrics->ascent; + ds = metrics->descent; + + gwd = MAX(metrics->width, grid->grid_width); + ght = MAX(metrics->height, grid->grid_height); + + /* + * Get the larger of the two dimensions. + */ + gsize = MAX(gwd, ght); + + nbpr = ((gsize * grid->bpp) + 7) >> 3; + bytes = (nbpr * gsize) << 1; + bitmap = (unsigned char *) malloc(bytes); + (void) memset((char *) bitmap, 0, bytes); + + /* + * Determine the new baseline. + */ + if (ht >= as + ds) + grid->base_y = (((ght >> 1) - (ht >> 1)) + ht) - ds; + else + grid->base_y = ((ght >> 1) - ((as + ds) >> 1)) + as; + + grid->base_x = (gwd >> 1) - (metrics->width >> 1); + if (metrics->x_offset < 0) + grid->base_x += MYABS(metrics->x_offset); + + nx = grid->base_x + grid->glyph_bbx.x_offset; + ny = grid->base_y - grid->glyph_bbx.ascent; + + /* + * Now copy the bitmap into the new storage base on the new metrics + * values. + */ + obpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + wd = grid->glyph_x + grid->glyph_bbx.width; + ht = grid->glyph_y + grid->glyph_bbx.height; + for (y = grid->glyph_y; y < ht; y++, ny++) { + col = grid->glyph_x * grid->bpp; + colx = nx * grid->bpp; + for (x = grid->glyph_x; x < wd; + x++, col += grid->bpp, colx += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = grid->bitmap[(y * obpr) + (col >> 3)] & masks[si]; + if (byte) { + di = (colx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + bitmap[(ny * nbpr) + (colx >> 3)] |= byte; + } + } + } + + /* + * Adjust the glyph coordinates. + */ + grid->glyph_x = grid->base_x + grid->glyph_bbx.x_offset; + grid->glyph_y = grid->base_y - grid->glyph_bbx.ascent; + + /* + * Get rid of the old grid bitmap and replace it with the new one. + */ + free((char *) grid->bitmap); + grid->bytes = bytes; + grid->bitmap = bitmap; + + /* + * Update the new grid width and height. + */ + grid->grid_width = grid->grid_height = gsize; + + /* + * Copy the metrics info into the font bounding box. + */ + grid->font_bbx.width = metrics->width; + grid->font_bbx.x_offset = metrics->x_offset; + grid->font_bbx.height = metrics->height; + grid->font_bbx.ascent = metrics->ascent; + grid->font_bbx.descent = metrics->descent; + grid->font_bbx.y_offset = metrics->y_offset; + } else { + /* + * The grid does not need to resized, but the baseline must + * be recalculated and the bitmap copied again. + */ + bytes = grid->bytes >> 1; + bitmap = grid->bitmap + bytes; + (void) memset((char *) bitmap, 0, bytes); + + ht = metrics->height; + as = metrics->ascent; + ds = metrics->descent; + + gwd = grid->grid_width; + ght = grid->grid_height; + + /* + * Determine the new baseline. + */ + if (ht >= as + ds) + grid->base_y = (((ght >> 1) - (ht >> 1)) + ht) - ds; + else + grid->base_y = ((ght >> 1) - ((as + ds) >> 1)) + as; + + grid->base_x = (gwd >> 1) - (metrics->width >> 1); + if (metrics->x_offset < 0) + grid->base_x += MYABS(metrics->x_offset); + + nx = grid->base_x + grid->glyph_bbx.x_offset; + ny = grid->base_y - grid->glyph_bbx.ascent; + + wd = grid->glyph_x + grid->glyph_bbx.width; + ht = grid->glyph_y + grid->glyph_bbx.height; + + obpr = nbpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + for (y = grid->glyph_y; y < ht; y++, ny++) { + col = grid->glyph_x * grid->bpp; + colx = nx * grid->bpp; + for (x = grid->glyph_x; x < wd; + x++, col += grid->bpp, colx += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = grid->bitmap[(y * obpr) + (col >> 3)] & masks[si]; + if (byte) { + di = (colx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + bitmap[(ny * nbpr) + (colx >> 3)] |= byte; + } + } + } + + /* + * Copy the adjusted bitmap back into the main area. + */ + (void) memcpy((char *) grid->bitmap, (char *) bitmap, bytes); + + /* + * Adjust the glyph coordinates. + */ + grid->glyph_x = grid->base_x + grid->glyph_bbx.x_offset; + grid->glyph_y = grid->base_y - grid->glyph_bbx.ascent; + + /* + * Copy the metrics info into the font bounding box. + */ + grid->font_bbx.width = metrics->width; + grid->font_bbx.x_offset = metrics->x_offset; + grid->font_bbx.height = metrics->height; + grid->font_bbx.ascent = metrics->ascent; + grid->font_bbx.descent = metrics->descent; + grid->font_bbx.y_offset = metrics->y_offset; + } + + /* + * If the font is not proportional, make sure the device width is adjusted + * to meet the new font bounding box. + */ + if (changed && grid->spacing != BDF_PROPORTIONAL) + grid->dwidth = grid->font_bbx.width; + + /* + * Always mark the grid as being modified on a resize. + */ + grid->modified = 1; + + return changed; +} + +int +#ifdef __STDC__ +bdf_grid_crop(bdf_glyph_grid_t *grid, int grid_modified) +#else +bdf_grid_crop(grid, grid_modified) +bdf_glyph_grid_t *grid; +int grid_modified; +#endif +{ + int cropped; + short x, y, delta, maxx, minx, maxy, miny, col; + unsigned short bpr; + unsigned char *masks; + + cropped = 0; + if (grid == 0) + return cropped; + + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + + maxx = maxy = -1; + minx = miny = grid->grid_width; + for (y = 0; y < grid->grid_height; y++) { + for (col = x = 0; x < grid->grid_width; x++, col += grid->bpp) { + if (grid->bitmap[(y * bpr) + (col >> 3)] & + masks[(col & 7) / grid->bpp]) { + minx = MIN(minx, x); + maxx = MAX(maxx, x); + miny = MIN(miny, y); + maxy = MAX(maxy, y); + } + } + } + + /* + * Handle an empty bitmap as a special case. + */ + if (maxx == -1) { + /* + * If the glyph bounding box indicated something was there originally, + * then indicate that it was cropped. + */ + if (grid->glyph_bbx.width != 0 || grid->glyph_bbx.height != 0) + cropped = 1; + (void) memset((char *) &grid->glyph_bbx, 0, sizeof(bdf_bbx_t)); + grid->glyph_x = grid->base_x; + grid->glyph_y = grid->base_y; + if (cropped) + grid->modified = 1; + return cropped; + } + + /* + * Increment the max points so width and height calculations won't go + * wrong. + */ + maxx++; + maxy++; + + if (minx != grid->glyph_x) { + cropped = 1; + delta = minx - grid->glyph_x; + grid->glyph_x += delta; + grid->glyph_bbx.x_offset += delta; + } + if (maxx - minx != grid->glyph_bbx.width) { + cropped = 1; + delta = (maxx - minx) - grid->glyph_bbx.width; + grid->glyph_bbx.width += delta; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth += delta; + } + + if (miny != grid->glyph_y) { + cropped = 1; + delta = miny - grid->glyph_y; + grid->glyph_y += delta; + grid->glyph_bbx.y_offset = + grid->base_y - (grid->glyph_y + (maxy - miny)); + } + if (maxy - miny != grid->glyph_bbx.height) { + cropped = 1; + delta = (maxy - miny) - grid->glyph_bbx.height; + grid->glyph_bbx.height += delta; + grid->glyph_bbx.y_offset = + grid->base_y - (grid->glyph_y + (maxy - miny)); + grid->glyph_bbx.ascent = + grid->glyph_bbx.height + grid->glyph_bbx.y_offset; + grid->glyph_bbx.descent = -grid->glyph_bbx.y_offset; + } + + /* + * Indicate that the grid was modified if the glyph had to be cropped. + */ + if (cropped && grid_modified) + grid->modified = 1; + + return cropped; +} + +/************************************************************************** + * + * Glyph grid pixel functions. + * + **************************************************************************/ + +int +#ifdef __STDC__ +bdf_grid_set_pixel(bdf_glyph_grid_t *grid, short x, short y, int val) +#else +bdf_grid_set_pixel(grid, x, y, val) +bdf_glyph_grid_t *grid; +short x, y; +int val; +#endif +{ + unsigned short si, di, dx; + int set, bpr, delta; + unsigned char *masks; + + set = 0; + + if (grid == 0 || x < 0 || x >= grid->grid_width || + y < 0 || y >= grid->grid_height) + return set; + + si = 0; + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; si = 7; break; + case 2: masks = twobpp; si = 3; break; + case 4: masks = fourbpp; si = 1; break; + } + + /* + * Remove any unused bits from the value. + */ + val &= masks[si]; + + dx = x * grid->bpp; + di = (dx & 7) / grid->bpp; + + /* + * Shift up the value to the appropriate place if necessary. + */ + if (di < si) + val <<= (si - di) * grid->bpp; + + /* + * Determine the bytes-per-row. + */ + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + + /* + * If the bit is already set, simply return with an indication that + * nothing changed. + */ + if ((grid->bitmap[(y * bpr) + (dx >> 3)] & masks[di]) == val) + return set; + + /* + * Set the bit. + */ + set = 1; + + /* + * Clear the bits that will take the new value. + */ + grid->bitmap[(y * bpr) + (dx >> 3)] &= ~masks[di]; + grid->bitmap[(y * bpr) + (dx >> 3)] |= val; + + /* + * Adjust the glyph bounding box. + */ + if (x < grid->glyph_x) { + delta = grid->glyph_x - x; + grid->glyph_bbx.width += delta; + grid->glyph_bbx.x_offset -= delta; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth = grid->glyph_bbx.width + grid->glyph_bbx.x_offset; + grid->glyph_x -= delta; + } else if (x >= grid->glyph_x + grid->glyph_bbx.width) { + delta = x - (grid->glyph_x + grid->glyph_bbx.width) + 1; + grid->glyph_bbx.width += delta; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth = grid->glyph_bbx.width + grid->glyph_bbx.x_offset; + } + if (y < grid->glyph_y) { + delta = grid->glyph_y - y; + grid->glyph_bbx.ascent += delta; + grid->glyph_bbx.height += delta; + grid->glyph_y -= delta; + } else if (y >= grid->glyph_y + grid->glyph_bbx.height) { + delta = y - (grid->glyph_y + grid->glyph_bbx.height) + 1; + grid->glyph_bbx.descent += delta; + grid->glyph_bbx.height += delta; + grid->glyph_bbx.y_offset = -grid->glyph_bbx.descent; + } + + /* + * Indicate that the glyph was modified. + */ + grid->modified = 1; + + return set; +} + +int +#ifdef __STDC__ +bdf_grid_clear_pixel(bdf_glyph_grid_t *grid, short x, short y) +#else +bdf_grid_clear_pixel(grid, x, y) +bdf_glyph_grid_t *grid; +short x, y; +#endif +{ + int cleared, bpr; + short delta, maxx, minx, maxy, miny, wd, ht; + unsigned short di, dx; + unsigned char *masks; + + cleared = 0; + + if (grid == 0 || x < 0 || x >= grid->grid_width || + y < 0 || y >= grid->grid_height) + return cleared; + + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + /* + * Determine the bytes-per-row. + */ + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + + dx = x * grid->bpp; + di = (dx & 7) / grid->bpp; + + /* + * If the bit is already clear, simply return with an indication that + * nothing changed. + */ + if (!(grid->bitmap[(y * bpr) + (dx >> 3)] & masks[di])) + return cleared; + + /* + * Clear the bit. + */ + cleared = 1; + grid->bitmap[(y * bpr) + (dx >> 3)] &= ~masks[di]; + + /* + * Determine the new min and max values. + */ + maxx = maxy = 0; + minx = miny = 32767; + + wd = grid->glyph_x + grid->glyph_bbx.width; + ht = grid->glyph_y + grid->glyph_bbx.height; + + for (y = grid->glyph_y; y < ht; y++) { + dx = grid->glyph_x * grid->bpp; + for (x = grid->glyph_x; x < wd; x++, dx += grid->bpp) { + di = (dx & 7) / grid->bpp; + if (grid->bitmap[(y * bpr) + (dx >> 3)] & masks[di]) { + minx = MIN(minx, x); + maxx = MAX(maxx, x); + miny = MIN(miny, y); + maxy = MAX(maxy, y); + } + } + } + + /* + * If this call clears the last bit in the image, set the glyph origin + * to the base and return. + */ + if (maxx == 0) { + grid->glyph_x = grid->base_x; + grid->glyph_y = grid->base_y; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth = 0; + (void) memset((char *) &grid->glyph_bbx, 0, sizeof(grid->glyph_bbx)); + grid->modified = 1; + return cleared; + } + + /* + * Figure out the left and right bearing changes. + */ + if (minx > grid->glyph_x) { + delta = minx - grid->glyph_x; + grid->glyph_bbx.width -= delta; + grid->glyph_bbx.x_offset += delta; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth = grid->glyph_bbx.width + grid->glyph_bbx.x_offset; + grid->glyph_x += delta; + } else if (maxx < wd - 1) { + delta = (wd - 1) - maxx; + grid->glyph_bbx.width -= delta; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth = grid->glyph_bbx.width + grid->glyph_bbx.x_offset; + } + + if (miny > grid->glyph_y) { + delta = miny - grid->glyph_y; + grid->glyph_bbx.ascent -= delta; + grid->glyph_bbx.height -= delta; + grid->glyph_y += delta; + } else if (maxy < ht - 1) { + delta = (ht - 1) - maxy; + grid->glyph_bbx.descent -= delta; + grid->glyph_bbx.height -= delta; + grid->glyph_bbx.y_offset = -grid->glyph_bbx.descent; + } + + /* + * Indicate that the glyph was modified. + */ + grid->modified = 1; + + return cleared; +} + +int +#ifdef __STDC__ +bdf_grid_invert_pixel(bdf_glyph_grid_t *grid, short x, short y, int val) +#else +bdf_grid_invert_pixel(grid, x, y, val) +bdf_glyph_grid_t *grid; +short x, y; +int val; +#endif +{ + short bpr, di; + unsigned char *masks; + + if (grid == 0 || x < 0 || x >= grid->grid_width || + y < 0 || y >= grid->grid_height) + return 0; + + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + /* + * Determine the bytes-per-row and mask index. + */ + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + di = ((x * grid->bpp) & 7) / grid->bpp; + + /* + * If the bit is set, then clear it, otherwise, set it. + */ + if (grid->bitmap[(y * bpr) + ((x * grid->bpp) >> 3)] & masks[di]) + return bdf_grid_clear_pixel(grid, x, y); + else + return bdf_grid_set_pixel(grid, x, y, val); +} + +/************************************************************************** + * + * Glyph grid bitmap transformation functions. + * + **************************************************************************/ + +short +#ifdef __STDC__ +_bdf_ceiling(double v) +#else +_bdf_ceiling(v) +double v; +#endif +{ + short val, neg; + + val = neg = 0; + if (v < 0) { + neg = 1; + while (v < -1.0) { + val++; + v += 1.0; + } + } else if (v > 0) { + while (v > 1.0) { + val++; + v -= 1.0; + } + if (v > 0.0) + val++; + } + return (!neg) ? val : -val; +} + +static int +#ifdef __STDC__ +_bdf_rotate_selection(bdf_glyph_grid_t *grid, int mul90, short degrees) +#else +_bdf_rotate_selection(grid, mul90, degrees) +bdf_glyph_grid_t *grid; +int mul90; +short degrees; +#endif +{ + int rotated, byte; + short wd, ht, nx, ny, cx, cy, x, y, col; + short ox, oy, shiftx, shifty, si, di; + double dx, dy; + unsigned short bytes, bpr; + unsigned char *scratch, *masks; + + rotated = 0; + + /* + * Check to see if the number of rotations would have no affect by + * checking if the count is a multiple of 4 (mod 4 == 0). + */ + if (grid == 0 || degrees == 0) + return rotated; + + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + bytes = grid->sel.bytes >> 1; + scratch = grid->sel.bitmap + bytes; + (void) memset((char *) scratch, 0, bytes); + + cx = grid->sel.width >> 1; + cy = grid->sel.height >> 1; + + wd = ht = MAX(grid->sel.width, grid->sel.height); + cx = cy = wd >> 1; + + bpr = ((wd * grid->bpp) + 7) >> 3; + + for (shiftx = shifty = y = 0; y < ht; y++) { + for (col = x = 0; x < wd; x++, col += grid->bpp) { + dx = (double) (x - cx); + dy = (double) (y - cy); + if (mul90) { + nx = cx + (short) ((dx * _bdf_cos_tbl[degrees]) - + (dy * _bdf_sin_tbl[degrees])); + ny = cy + (short) ((dx * _bdf_sin_tbl[degrees]) + + (dy * _bdf_cos_tbl[degrees])); + } else { + nx = cx + _bdf_ceiling((dx * _bdf_cos_tbl[degrees]) - + (dy * _bdf_sin_tbl[degrees])); + ny = cy + _bdf_ceiling((dx * _bdf_sin_tbl[degrees]) + + (dy * _bdf_cos_tbl[degrees])); + } + + /* + * Wrap the coordinates around the edges if necessary. + */ + if (nx < 0) { + shiftx = MIN(shiftx, nx); + nx += wd; + } else if (nx >= wd) { + ox = (nx - wd) + 1; + shiftx = MAX(shiftx, ox); + nx -= wd; + } + if (ny < 0) { + shifty = MIN(shifty, ny); + ny += ht; + } else if (ny >= ht) { + oy = (ny - ht) + 1; + shifty = MAX(shifty, oy); + ny -= ht; + } + + si = (col & 7) / grid->bpp; + byte = grid->sel.bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + rotated = 1; + nx *= grid->bpp; + di = (nx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + scratch[(ny * bpr) + (nx >> 3)] |= byte; + } + } + } + + if (rotated) { + /* + * If a shift is required, then shift the scratch area back into + * the main bitmap. + */ + if (shiftx || shifty) { + (void) memset((char *) grid->sel.bitmap, 0, bytes); + for (y = 0; y < ht; y++) { + for (col = x = 0; x < wd; x++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = scratch[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + nx = x - shiftx; + ny = y - shifty; + + if (nx < 0) + nx += wd; + else if (nx >= wd) + nx -= wd; + if (ny < 0) + ny += ht; + else if (ny >= ht) + ny -= ht; + + nx *= grid->bpp; + di = (nx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + grid->sel.bitmap[(ny * bpr) + (nx >> 3)] |= byte; + } + } + } + } else + /* + * Copy the scratch buffer back to the main buffer. + */ + (void) memcpy((char *) grid->sel.bitmap, (char *) scratch, bytes); + + /* + * Determine the new selection width and height. + */ + ox = oy = 0; + nx = ny = 16384; + for (y = 0; y < ht; y++) { + for (col = x = 0; x < wd; x++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + if (grid->sel.bitmap[(y * bpr) + (col >> 3)] & masks[si]) { + ox = MAX(ox, x); + nx = MIN(nx, x); + oy = MAX(oy, y); + ny = MIN(ny, y); + } + } + } + + /* + * Recalculate the center corrdinates so the selection will be + * positioned nicely once it is shifted to the upper left corner. + */ + cx = grid->sel.width >> 1; + cy = grid->sel.height >> 1; + + /* + * Set the new width and height. + */ + grid->sel.width = (ox - nx) + 1; + grid->sel.height = (oy - ny) + 1; + + /* + * Shift again to force the selection to the upper left corner. + */ + if (nx || ny) { + (void) memset((char *) scratch, 0, bytes); + for (y = 0; y < ht; y++) { + for (col = x = 0; x < wd; x++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = grid->sel.bitmap[(y * bpr) + (col >> 3)] & + masks[si]; + if (byte) { + oy = y - ny; + ox = (x - nx) * grid->bpp; + di = (ox & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + scratch[(oy * bpr) + (ox >> 3)] |= byte; + } + } + } + (void) memcpy((char *) grid->sel.bitmap, (char *) scratch, bytes); + } + + /* + * Determine the new top left coordinates from the center coordinates. + */ + grid->sel.x = (grid->sel.x + cx) - (grid->sel.width >> 1); + grid->sel.y = (grid->sel.y + cy) - (grid->sel.height >> 1); + + /* + * If the rotation caused the selection rectangle to overlap the edges + * of the grid, shift it so it is completely visible again. + */ + if (grid->sel.x + grid->sel.width > grid->grid_width) + grid->sel.x -= (grid->sel.x + grid->sel.width) - grid->grid_width; + if (grid->sel.y + grid->sel.height > grid->grid_height) + grid->sel.y -= (grid->sel.y + grid->sel.height) - grid->grid_height; + + /* + * Mark the grid as being modified. + */ + grid->modified = 1; + } + + return rotated; +} + +static void +#ifdef __STDC__ +_bdf_rotate_resize(bdf_glyph_grid_t *grid, int mul90, short degrees, + int *resize) +#else +_bdf_rotate_resize(grid, mul90, degrees, resize) +bdf_glyph_grid_t *grid; +int mul90; +short degrees; +int *resize; +#endif +{ + unsigned short wd, ht; + short cx, cy, x1, y1, x2, y2; + double dx1, dy1, dx2, dy2; + bdf_metrics_t metrics; + + *resize = 0; + (void) memset((char *) &metrics, 0, sizeof(bdf_metrics_t)); + + metrics.x_offset = grid->font_bbx.x_offset; + metrics.width = grid->font_bbx.width; + metrics.ascent = grid->font_bbx.ascent; + metrics.descent = grid->font_bbx.descent; + metrics.height = grid->font_bbx.height; + metrics.y_offset = grid->font_bbx.y_offset; + + cx = grid->glyph_x + (grid->glyph_bbx.width >> 1); + cy = grid->glyph_y + (grid->glyph_bbx.height >> 1); + + /* + * Rotate the lower left and upper right corners and check for a potential + * resize. + */ + x1 = grid->glyph_x; + y1 = grid->glyph_y + grid->glyph_bbx.height; + x2 = grid->glyph_x + grid->glyph_bbx.width; + y2 = grid->glyph_y; + + dx1 = (double) (x1 - cx); + dy1 = (double) (y1 - cy); + dx2 = (double) (x2 - cx); + dy2 = (double) (y2 - cx); + + if (mul90) { + x1 = cx + (short) ((dx1 * _bdf_cos_tbl[degrees]) - + (dy1 * _bdf_sin_tbl[degrees])); + y1 = cy + (short) ((dx1 * _bdf_sin_tbl[degrees]) + + (dy1 * _bdf_cos_tbl[degrees])); + x2 = cx + (short) ((dx2 * _bdf_cos_tbl[degrees]) - + (dy2 * _bdf_sin_tbl[degrees])); + y2 = cy + (short) ((dx2 * _bdf_sin_tbl[degrees]) + + (dy2 * _bdf_cos_tbl[degrees])); + } else { + x1 = cx + _bdf_ceiling((dx1 * _bdf_cos_tbl[degrees]) - + (dy1 * _bdf_sin_tbl[degrees])); + y1 = cy + _bdf_ceiling((dx1 * _bdf_sin_tbl[degrees]) + + (dy1 * _bdf_cos_tbl[degrees])); + x2 = cx + _bdf_ceiling((dx2 * _bdf_cos_tbl[degrees]) - + (dy2 * _bdf_sin_tbl[degrees])); + y2 = cy + _bdf_ceiling((dx2 * _bdf_sin_tbl[degrees]) + + (dy2 * _bdf_cos_tbl[degrees])); + } + + wd = MYABS(x2 - x1); + ht = MYABS(y2 - y1); + if (wd > metrics.width) { + metrics.width += wd - grid->font_bbx.width; + *resize = 1; + } + if (ht > metrics.height) { + metrics.ascent += ht - grid->font_bbx.height; + metrics.height += ht - grid->font_bbx.height; + *resize = 1; + } + + /* + * Rotate the upper left and lower right corners and check for a potential + * resize. + */ + x1 = grid->glyph_x; + y1 = grid->glyph_y; + x2 = grid->glyph_x + grid->glyph_bbx.width; + y2 = grid->glyph_y + grid->glyph_bbx.height; + + dx1 = (double) (x1 - cx); + dy1 = (double) (y1 - cy); + dx2 = (double) (x2 - cx); + dy2 = (double) (y2 - cx); + + if (mul90) { + x1 = cx + (short) ((dx1 * _bdf_cos_tbl[degrees]) - + (dy1 * _bdf_sin_tbl[degrees])); + y1 = cy + (short) ((dx1 * _bdf_sin_tbl[degrees]) + + (dy1 * _bdf_cos_tbl[degrees])); + x2 = cx + (short) ((dx2 * _bdf_cos_tbl[degrees]) - + (dy2 * _bdf_sin_tbl[degrees])); + y2 = cy + (short) ((dx2 * _bdf_sin_tbl[degrees]) + + (dy2 * _bdf_cos_tbl[degrees])); + } else { + x1 = cx + _bdf_ceiling((dx1 * _bdf_cos_tbl[degrees]) - + (dy1 * _bdf_sin_tbl[degrees])); + y1 = cy + _bdf_ceiling((dx1 * _bdf_sin_tbl[degrees]) + + (dy1 * _bdf_cos_tbl[degrees])); + x2 = cx + _bdf_ceiling((dx2 * _bdf_cos_tbl[degrees]) - + (dy2 * _bdf_sin_tbl[degrees])); + y2 = cy + _bdf_ceiling((dx2 * _bdf_sin_tbl[degrees]) + + (dy2 * _bdf_cos_tbl[degrees])); + } + + wd = MYABS(x2 - x1); + ht = MYABS(y2 - y1); + if (wd > metrics.width) { + metrics.width += wd - grid->font_bbx.width; + *resize = 1; + } + if (ht > metrics.height) { + metrics.ascent += ht - grid->font_bbx.height; + metrics.height += ht - grid->font_bbx.height; + *resize = 1; + } + + if (*resize) + (void) bdf_grid_resize(grid, &metrics); +} + +static void +#ifdef __STDC__ +_bdf_shear_resize(bdf_glyph_grid_t *grid, short degrees, int neg, int *resize) +#else +_bdf_shear_resize(grid, degrees, neg, resize) +bdf_glyph_grid_t *grid; +short degrees; +int neg, *resize; +#endif +{ + unsigned short wd; + short x1, y1, x2, y2; + bdf_metrics_t metrics; + + *resize = 0; + (void) memset((char *) &metrics, 0, sizeof(bdf_metrics_t)); + + metrics.x_offset = grid->font_bbx.x_offset; + metrics.width = grid->font_bbx.width; + metrics.ascent = grid->font_bbx.ascent; + metrics.descent = grid->font_bbx.descent; + metrics.height = grid->font_bbx.height; + metrics.y_offset = grid->font_bbx.y_offset; + + /* + * Shear the lower left and upper right corners and check for a potential + * resize. + */ + x1 = 0; + y1 = grid->glyph_bbx.height; + x2 = grid->glyph_bbx.width; + y2 = 0; + + if (neg) { + x1 += (short) ((double) y1 * _bdf_tan_tbl[degrees]); + x2 += (short) ((double) y2 * _bdf_tan_tbl[degrees]); + } else { + x1 += (short) ((double) (grid->glyph_bbx.height - y1) * + _bdf_tan_tbl[degrees]); + x2 += (short) ((double) (grid->glyph_bbx.height - y2) * + _bdf_tan_tbl[degrees]); + } + + wd = MYABS(x2 - x1); + if (wd > metrics.width) { + metrics.width += wd - grid->font_bbx.width; + *resize = 1; + } + + /* + * Shear the upper left and lower right corners and check for a potential + * resize. + */ + x1 = 0; + y1 = 0; + x2 = grid->glyph_bbx.width; + y2 = grid->glyph_bbx.height; + + if (neg) { + x1 += (short) ((double) y1 * _bdf_tan_tbl[degrees]); + x2 += (short) ((double) y2 * _bdf_tan_tbl[degrees]); + } else { + x1 += (short) ((double) (grid->glyph_bbx.height - y1) * + _bdf_tan_tbl[degrees]); + x2 += (short) ((double) (grid->glyph_bbx.height - y2) * + _bdf_tan_tbl[degrees]); + } + + wd = MYABS(x2 - x1); + if (wd > metrics.width) { + metrics.width += wd - grid->font_bbx.width; + *resize = 1; + } + + if (*resize) + (void) bdf_grid_resize(grid, &metrics); +} + +/* + * Rotate the bitmap in the grid by some number of degrees. + */ +int +#ifdef __STDC__ +bdf_grid_rotate(bdf_glyph_grid_t *grid, short degrees, int *resize) +#else +bdf_grid_rotate(grid, degrees, resize) +bdf_glyph_grid_t *grid; +short degrees; +int *resize; +#endif +{ + int rotated, mul90; + short nx, ny, cx, cy, x, y, wd, ht; + short ox, oy, gx, gy, shiftx, shifty; + unsigned short si, di, col, byte; + double dx, dy; + unsigned short bytes, bpr; + unsigned char *scratch, *masks; + + rotated = 0; + + /* + * Make sure the number of degrees is between 0 and 359 and adjusted to a + * positive number of degrees if necessary. + */ + while (degrees < 0) + degrees += 360; + while (degrees >= 360) + degrees -= 360; + + if (grid == 0 || degrees == 0 || + (grid->glyph_bbx.width == 0 && grid->glyph_bbx.height == 0)) + return rotated; + + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + mul90 = ((degrees % 90) == 0) ? 1 : 0; + + /* + * Force the grid to resize if the rotation requires it. + */ + _bdf_rotate_resize(grid, mul90, degrees, resize); + + if (grid->sel.width != 0 && grid->sel.height != 0) + return _bdf_rotate_selection(grid, mul90, degrees); + + /* + * Halve the byte count in the grid for later use. + */ + bytes = grid->bytes >> 1; + + /* + * Point at the scratch buffer area and initialize it. + */ + scratch = grid->bitmap + bytes; + (void) memset((char *) scratch, 0, bytes); + + /* + * Determine the bytes per row. + */ + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + + /* + * Determine the center coordinates of the glyph bitmap rectangle. + */ + cx = grid->glyph_x + (grid->glyph_bbx.width >> 1); + cy = grid->glyph_y + (grid->glyph_bbx.height >> 1); + + /* + * Only run over the rectangle containing the glyph itself. + */ + gx = grid->glyph_x; + gy = grid->glyph_y; + + wd = gx + grid->glyph_bbx.width; + ht = gy + grid->glyph_bbx.height; + + /* + * Initialize the adjustment counts used if the bitmap + * wraps around the edge. + */ + shiftx = shifty = 0; + + for (y = gy; y < ht; y++) { + col = gx * grid->bpp; + for (x = gx; x < wd; x++, col += grid->bpp) { + + /* + * Rotate the point. + */ + dx = (double) (x - cx); + dy = (double) (y - cy); + if (mul90) { + nx = cx + (short) ((dx * _bdf_cos_tbl[degrees]) - + (dy * _bdf_sin_tbl[degrees])); + ny = cy + (short) ((dx * _bdf_sin_tbl[degrees]) + + (dy * _bdf_cos_tbl[degrees])); + } else { + nx = cx + _bdf_ceiling((dx * _bdf_cos_tbl[degrees]) - + (dy * _bdf_sin_tbl[degrees])); + ny = cy + _bdf_ceiling((dx * _bdf_sin_tbl[degrees]) + + (dy * _bdf_cos_tbl[degrees])); + } + + /* + * Wrap the coordinates around the edges if necessary. + */ + if (nx < 0) { + shiftx = MIN(shiftx, nx); + nx += grid->grid_width; + } else if (nx >= grid->grid_width) { + ox = (nx - grid->grid_width) + 1; + shiftx = MAX(shiftx, ox); + nx -= grid->grid_width; + } + if (ny < 0) { + shifty = MIN(shifty, ny); + ny += grid->grid_height; + } else if (ny >= grid->grid_height) { + oy = (ny - grid->grid_height) + 1; + shifty = MAX(shifty, oy); + ny -= grid->grid_height; + } + + si = (col & 7) / grid->bpp; + byte = grid->bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + rotated = 1; + nx *= grid->bpp; + di = (nx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + scratch[(ny * bpr) + (nx >> 3)] |= byte; + } + } + } + + if (rotated) { + /* + * If a shift is required, then shift the scratch area back into + * the main bitmap. + */ + if (shiftx || shifty) { + (void) memset((char *) grid->bitmap, 0, bytes); + for (y = 0; y < grid->grid_height; y++) { + for (col = x = 0; x < grid->grid_width; + x++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = scratch[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + nx = x - shiftx; + ny = y - shifty; + + if (nx < 0) + nx += grid->grid_width; + else if (nx >= grid->grid_width) + nx -= grid->grid_width; + if (ny < 0) + ny += grid->grid_height; + else if (ny >= grid->grid_height) + ny -= grid->grid_height; + + nx *= grid->bpp; + di = (nx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + grid->bitmap[(ny * bpr) + (nx >> 3)] |= byte; + } + } + } + } else + /* + * Copy the scratch buffer back to the main buffer. + */ + (void) memcpy((char *) grid->bitmap, (char *) scratch, bytes); + + /* + * Determine the new glyph bounding box and the top left coordinates. + */ + ox = oy = 0; + nx = ny = 16384; + for (y = 0; y < grid->grid_height; y++) { + for (col = x = 0; x < grid->grid_width; x++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + if (grid->bitmap[(y * bpr) + (col >> 3)] & masks[si]) { + nx = MIN(nx, x); + ox = MAX(ox, x); + ny = MIN(ny, y); + oy = MAX(oy, y); + } + } + } + + /* + * Set the new top left corrdinates. + */ + grid->glyph_x = nx; + grid->glyph_y = ny; + + /* + * Set the new glyph bounding box. + */ + grid->glyph_bbx.width = (ox - nx) + 1; + grid->glyph_bbx.x_offset = nx - grid->base_x; + grid->glyph_bbx.height = (oy - ny) + 1; + grid->glyph_bbx.ascent = grid->base_y - ny; + grid->glyph_bbx.descent = grid->glyph_bbx.height - + grid->glyph_bbx.ascent; + grid->glyph_bbx.y_offset = -grid->glyph_bbx.descent; + + /* + * Mark the grid as being modified. + */ + grid->modified = 1; + } + + return rotated; +} + +int +#ifdef __STDC__ +bdf_grid_shear(bdf_glyph_grid_t *grid, short degrees, int *resize) +#else +bdf_grid_shear(grid, degrees, resize) +bdf_glyph_grid_t *grid; +short degrees; +int *resize; +#endif +{ + int sheared, neg; + short cx, cy, wd, ht, gx, gy, x, y; + short nx, ox, ny, oy, shiftx, shifty; + unsigned short bytes, bpr, si, di, col, byte; + unsigned char *scratch, *masks; + + sheared = 0; + + if (degrees == 0 || degrees < -45 || degrees > 45 || grid == 0 || + (grid->glyph_bbx.width == 0 && grid->glyph_bbx.height == 0)) + return sheared; + + if ((neg = (degrees < 0))) + degrees = -degrees; + + /* + * Check to see if the grid needs to be resized to hold the sheared glyph. + */ + _bdf_shear_resize(grid, degrees, neg, resize); + + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + /* + * Halve the byte count in the grid for later use. + */ + bytes = grid->bytes >> 1; + + /* + * Point at the scratch buffer area and initialize it. + */ + scratch = grid->bitmap + bytes; + (void) memset((char *) scratch, 0, bytes); + + /* + * Determine the bytes per row. + */ + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + + /* + * Determine the center coordinates of the glyph bitmap rectangle. + */ + gx = grid->glyph_x; + gy = grid->glyph_y; + + cx = gx + (grid->glyph_bbx.width >> 1); + cy = gy + (grid->glyph_bbx.height >> 1); + + wd = gx + grid->glyph_bbx.width; + ht = gy + grid->glyph_bbx.height; + + shiftx = shifty = 0; + for (y = gy; y < ht; y++) { + col = gx * grid->bpp; + for (x = gx; x < wd; x++, col += grid->bpp) { + ny = y; + if (neg) + nx = x + (short) ((double) y * _bdf_tan_tbl[degrees]); + else + nx = x + (short) ((double) (gy + (ht - y)) * + _bdf_tan_tbl[degrees]); + + if (nx < 0) { + shiftx = MIN(shiftx, nx); + nx += grid->grid_width; + } else if (nx >= grid->grid_width) { + ox = (nx - grid->grid_width) + 1; + shiftx = MAX(shiftx, ox); + nx -= grid->grid_width; + } + if (ny < 0) { + shifty = MIN(shifty, ny); + ny += grid->grid_height; + } else if (ny >= grid->grid_height) { + oy = (ny - grid->grid_height) + 1; + shifty = MAX(shifty, oy); + ny -= grid->grid_height; + } + + si = (col & 7) / grid->bpp; + byte = grid->bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + sheared = 1; + nx *= grid->bpp; + di = (nx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + scratch[(y * bpr) + (nx >> 3)] |= byte; + } + } + } + + if (sheared) { + /* + * If a shift is required, then shift the scratch area back into + * the main bitmap. + */ + if (shiftx || shifty) { + (void) memset((char *) grid->bitmap, 0, bytes); + for (y = 0; y < grid->grid_height; y++) { + for (col = x = 0; x < grid->grid_width; + x++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = scratch[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + nx = x - shiftx; + ny = y - shifty; + + if (nx < 0) + nx += grid->grid_width; + else if (nx >= grid->grid_width) + nx -= grid->grid_width; + if (ny < 0) + ny += grid->grid_height; + else if (ny >= grid->grid_height) + ny -= grid->grid_height; + + nx *= grid->bpp; + di = (nx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + grid->bitmap[(ny * bpr) + (nx >> 3)] |= byte; + } + } + } + } else + /* + * Copy the scratch buffer back to the main buffer. + */ + (void) memcpy((char *) grid->bitmap, (char *) scratch, bytes); + + ox = oy = 0; + nx = ny = 16384; + for (y = 0; y < grid->grid_height; y++) { + for (col = x = 0; x < grid->grid_width; x++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + if (grid->bitmap[(y * bpr) + (col >> 3)] & masks[si]) { + ox = MAX(ox, x); + nx = MIN(nx, x); + oy = MAX(oy, y); + ny = MIN(ny, y); + } + } + } + + /* + * Set the new top left corrdinates. + */ + grid->glyph_x = nx; + grid->glyph_y = ny; + + /* + * Set the new glyph bounding box. + */ + grid->glyph_bbx.width = (ox - nx) + 1; + grid->glyph_bbx.x_offset = nx - grid->base_x; + grid->glyph_bbx.height = (oy - ny) + 1; + grid->glyph_bbx.ascent = grid->base_y - ny; + grid->glyph_bbx.descent = grid->glyph_bbx.height - + grid->glyph_bbx.ascent; + grid->glyph_bbx.y_offset = -grid->glyph_bbx.descent; + + /* + * Mark the grid as being modified. + */ + grid->modified = 1; + } + + return sheared; +} + +int +#ifdef __STDC__ +bdf_grid_embolden(bdf_glyph_grid_t *grid) +#else +bdf_grid_embolden(grid) +bdf_glyph_grid_t *grid; +#endif +{ + int done; + short wd, ht, gx, gy, x, y; + unsigned short b1, b2, bpr, si, di, col; + unsigned char *masks; + + done = 0; + + if (grid == 0 || + (grid->glyph_bbx.width == 0 && grid->glyph_bbx.height == 0)) + return done; + + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + /* + * Determine the bytes per row. + */ + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + + gx = grid->glyph_x; + gy = grid->glyph_y; + + wd = gx + grid->glyph_bbx.width; + ht = gy + grid->glyph_bbx.height; + + if (grid->spacing == BDF_PROPORTIONAL || + (grid->spacing == BDF_MONOWIDTH && + grid->glyph_bbx.width < grid->font_bbx.width)) + /* + * Only allow horizontal expansion in the cases that make sense. + */ + wd++; + + for (y = gy; y < ht; y++) { + col = (wd - 1) * grid->bpp; + for (x = wd - 1; x > gx; x--, col -= grid->bpp) { + si = (col & 7) / grid->bpp; + di = ((col - grid->bpp) & 7) / grid->bpp; + b1 = grid->bitmap[(y * bpr) + (col >> 3)] & masks[si]; + b2 = grid->bitmap[(y * bpr) + ((col - grid->bpp) >> 3)] & + masks[di]; + if (!b1 && b2) { + if (di < si) + b2 >>= (si - di) * grid->bpp; + else if (di > si) + b2 <<= (di - si) * grid->bpp; + grid->bitmap[(y * bpr) + (col >> 3)] |= b2; + /* + * Mark the grid as being modified. + */ + done = grid->modified = 1; + } + } + } + + /* + * Adjust the glyph width so it will be reflected when the glyph is stored + * back in the font. + */ + grid->glyph_bbx.width = wd - gx; + + return done; +} + +/************************************************************************** + * + * Glyph grid selection functions. + * + **************************************************************************/ + +int +#ifdef __STDC__ +bdf_has_selection(bdf_glyph_grid_t *grid, short *x, short *y, + short *width, short *height) +#else +bdf_has_selection(grid, x, y, width, height) +bdf_glyph_grid_t *grid; +short *x, *y, *width, *height; +#endif +{ + if (grid == 0 || (grid->sel.width == 0 && grid->sel.height == 0)) + return 0; + + if (x != 0) + *x = grid->sel.x; + if (y != 0) + *y = grid->sel.y; + if (width != 0) + *width = grid->sel.width; + if (height != 0) + *height = grid->sel.height; + + return 1; +} + +/* + * Select a rectangle on the grid. + */ +void +#ifdef __STDC__ +bdf_set_selection(bdf_glyph_grid_t *grid, short x, short y, + short width, short height) +#else +bdf_set_selection(grid, x, y, width, height) +bdf_glyph_grid_t *grid; +short x, y, width, height; +#endif +{ + short nx, ny, wd, ht, ssize, dx, dy, col; + unsigned short bytes, bpr, sbpr, si, di, byte; + unsigned char *masks; + + if (grid == 0) + return; + + /* + * Make sure the specified rectangle is within reasonable bounds. + */ + if (x < 0 || x >= grid->grid_width) + x = 0; + if (y < 0 || y >= grid->grid_height) + y = 0; + + if (x + width > grid->grid_width) + width = (x + width) - grid->grid_width; + if (y + height > grid->grid_height) + height = (y + height) - grid->grid_height; + + grid->sel.x = x; + grid->sel.y = y; + grid->sel.width = width; + grid->sel.height = height; + + /* + * Allocate enough space to represent a square the size of the largest + * of the width and height of the selection. This allows rotation and + * flipping of the selected bitmap. + */ + ssize = MAX(width, height); + + bytes = ((((ssize * grid->bpp) + 7) >> 3) * ssize) << 1; + + /* + * If the selection is being removed (width and height are 0), then simply + * return. + */ + if (bytes == 0) + return; + + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + if (bytes > grid->sel.bytes) { + if (grid->sel.bytes == 0) + grid->sel.bitmap = (unsigned char *) malloc(bytes); + else + grid->sel.bitmap = (unsigned char *) + realloc((char *) grid->sel.bitmap, bytes); + grid->sel.bytes = bytes; + } else + bytes = grid->sel.bytes; + + /* + * Initialize the selection bitmap and copy the selected bits to it. + */ + (void) memset((char *) grid->sel.bitmap, 0, bytes); + + wd = x + width; + ht = y + height; + + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + sbpr = ((grid->sel.width * grid->bpp) + 7) >> 3; + + for (ny = 0, dy = y; dy < ht; dy++, ny++) { + col = x * grid->bpp; + for (nx = 0, dx = x; dx < wd; + dx++, nx += grid->bpp, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = grid->bitmap[(dy * bpr) + (col >> 3)] & masks[si]; + if (byte) { + di = (nx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + grid->sel.bitmap[(ny * sbpr) + (nx >> 3)] |= byte; + } + } + } +} + +/* + * Detach a selection in preparation for moving it. What is does is clear the + * bits set in the selection from the main grid. Again, this is only used for + * move operations. + */ +void +#ifdef __STDC__ +bdf_detach_selection(bdf_glyph_grid_t *grid) +#else +bdf_detach_selection(grid) +bdf_glyph_grid_t *grid; +#endif +{ + short sx, sy, x, y, wd, ht, dx; + unsigned short bpr, sbpr, si, di, byte; + unsigned char *masks; + + if (grid == 0 || (grid->sel.width == 0 && grid->sel.height == 0)) + return; + + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + sbpr = ((grid->sel.width * grid->bpp) + 7) >> 3; + + wd = grid->sel.x + grid->sel.width; + ht = grid->sel.y + grid->sel.height; + + for (sy = 0, y = grid->sel.y; y < ht; y++, sy++) { + for (sx = 0, x = grid->sel.x; x < wd; x++, sx += grid->bpp) { + si = (sx & 7) / grid->bpp; + byte = grid->sel.bitmap[(sy * sbpr) + (sx >> 3)] & masks[si]; + if (byte) { + dx = x * grid->bpp; + di = (dx & 7) / grid->bpp; + grid->bitmap[(y * bpr) + (dx >> 3)] &= ~masks[di]; + } + } + } + + /* + * Crop the new image to determine the new bounds with the selection. + */ + (void) bdf_grid_crop(grid, 1); +} + +void +#ifdef __STDC__ +bdf_attach_selection(bdf_glyph_grid_t *grid) +#else +bdf_attach_selection(grid) +bdf_glyph_grid_t *grid; +#endif +{ + short sx, sy, x, y, wd, ht; + unsigned short bpr, sbpr, dx, di, si, byte; + unsigned char *masks; + + if (grid == 0 || (grid->sel.width == 0 && grid->sel.height == 0)) + return; + + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + sbpr = ((grid->sel.width * grid->bpp) + 7) >> 3; + + wd = grid->sel.x + grid->sel.width; + ht = grid->sel.y + grid->sel.height; + + for (sy = 0, y = grid->sel.y; y < ht; y++, sy++) { + for (sx = 0, x = grid->sel.x; x < wd; x++, sx += grid->bpp) { + si = (sx & 7) / grid->bpp; + byte = grid->sel.bitmap[(sy * sbpr) + (sx >> 3)] & masks[si]; + if (byte) { + dx = x * grid->bpp; + di = (dx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + grid->bitmap[(y * bpr) + (dx >> 3)] |= byte; + } + } + } + + /* + * Crop the new image to determine the new bounds with the selection. + */ + (void) bdf_grid_crop(grid, 1); +} + +/* + * Indicate the selection no longer exists by setting the width and height to + * 0. + */ +void +#ifdef __STDC__ +bdf_lose_selection(bdf_glyph_grid_t *grid) +#else +bdf_lose_selection(grid) +bdf_glyph_grid_t *grid; +#endif +{ + if (grid == 0) + return; + grid->sel.width = grid->sel.height = 0; +} + +/* + * Delete the selection by first detaching it which will erase the rectangle + * on the grid and then losing the selection. + */ +void +#ifdef __STDC__ +bdf_delete_selection(bdf_glyph_grid_t *grid) +#else +bdf_delete_selection(grid) +bdf_glyph_grid_t *grid; +#endif +{ + bdf_detach_selection(grid); + bdf_lose_selection(grid); +} + +/* + * Check to see if a coordinate pair is in the selected region. + */ +int +#ifdef __STDC__ +bdf_in_selection(bdf_glyph_grid_t *grid, short x, short y, short *set) +#else +bdf_in_selection(grid, x, y, set) +bdf_glyph_grid_t *grid; +short x, y, *set; +#endif +{ + short wd, ht; + unsigned short bpr, si, di, byte; + unsigned char *masks; + + if (grid == 0 || (grid->sel.width == 0 && grid->sel.height == 0)) + return 0; + + di = 0; + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; di = 7; break; + case 2: masks = twobpp; di = 3; break; + case 4: masks = fourbpp; di = 1; break; + } + + bpr = ((grid->sel.width * grid->bpp) + 7) >> 3; + + wd = grid->sel.x + grid->sel.width; + ht = grid->sel.y + grid->sel.height; + + if ((x >= grid->sel.x && x < wd) && (y >= grid->sel.y && y < ht)) { + if (set) { + /* + * Adjust the byte back to an index value. + */ + x *= grid->bpp; + si = (x & 7) / grid->bpp; + byte = grid->sel.bitmap[(y * bpr) + (x >> 3)] & masks[si]; + if (di > si) + byte >>= (di - si) * grid->bpp; + *set = byte; + } + return 1; + } + + return 0; +} + +int +#ifdef __STDC__ +bdf_grid_shift(bdf_glyph_grid_t *grid, short xcount, short ycount) +#else +bdf_grid_shift(grid, xcount, ycount) +bdf_glyph_grid_t *grid; +short xcount, ycount; +#endif +{ + int sel, delta; + short xdir, ydir, x, y, wd, ht, dx, dy, nx, ny; + unsigned short bytes, bpr, si, di, byte, col; + unsigned char *scratch, *masks; + + if (grid == 0) + return 0; + + xdir = ydir = 1; + if (xcount < 0) { + xdir = -1; + xcount = -xcount; + } + + if (ycount < 0) { + ydir = -1; + ycount = -ycount; + } + + /* + * Adjust the shift counts if they are larger than they should be. + */ + if (xcount > grid->grid_width) + xcount -= grid->grid_width; + if (ycount > grid->grid_height) + ycount -= grid->grid_height; + + /* + * Adjust the counts to limit the shift to the boundaries of the grid. + */ + if (grid->sel.width != 0 && grid->sel.height != 0) { + /* + * The selection is being shifted. + */ + x = grid->sel.x; + y = grid->sel.y; + wd = grid->sel.width; + ht = grid->sel.height; + sel = 1; + } else { + x = grid->glyph_x; + y = grid->glyph_y; + wd = grid->glyph_bbx.width; + ht = grid->glyph_bbx.height; + sel = 0; + } + + /* + * If the width and height are 0, then simply return, because there + * is nothing to shift. + */ + if (wd == 0 && ht == 0) + return 0; + + if (xdir == 1 && x + wd + xcount > grid->grid_width) + xcount = grid->grid_width - (x + wd); + else if (xdir == -1 && xcount > x) + xcount = x; + + if (ydir == 1 && y + ht + ycount > grid->grid_height) + ycount = grid->grid_height - (y + ht); + else if (ydir == -1 && ycount > y) + ycount = y; + + if (xcount == 0 && ycount == 0) + return 0; + + /* + * If the selection is the one being shifted, adjust the X and Y + * coordinates and adjust the glyph metrics. + */ + if (sel) { + /* + * Determine the actual ink bounds of the selection so the + * glyph metrics can be adjusted if necessary. + */ + if (_bdf_grid_ink_bounds(grid, &x, &y, &wd, &ht)) { + /* + * Have to adjust the glyph metrics. + */ + x += xdir * xcount; + y += ydir * ycount; + if (x < grid->glyph_x) { + delta = grid->glyph_x - x; + grid->glyph_bbx.width += delta; + grid->glyph_bbx.x_offset -= delta; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth += delta; + grid->glyph_x -= delta; + } else if (x >= grid->glyph_x + grid->glyph_bbx.width) { + delta = x - (grid->glyph_x + grid->glyph_bbx.width); + grid->glyph_bbx.width += delta; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth += delta; + } + + if (y < grid->glyph_y) { + delta = grid->glyph_y - y; + grid->glyph_bbx.height += delta; + grid->glyph_bbx.ascent += delta; + grid->glyph_y -= delta; + } else if (y + ht >= grid->glyph_y + grid->glyph_bbx.height) { + delta = (y + ht) - (grid->glyph_y + grid->glyph_bbx.height); + grid->glyph_bbx.height += delta; + grid->glyph_bbx.y_offset -= delta; + grid->glyph_bbx.descent += delta; + } + + grid->modified = 1; + } + + /* + * Adjust the top-left coordinate of the selection rectangle. + */ + grid->sel.x += xdir * xcount; + grid->sel.y += ydir * ycount; + + return 1; + } + + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; di = 7; break; + case 2: masks = twobpp; di = 3; break; + case 4: masks = fourbpp; di = 1; break; + } + + /* + * The glyph itself is being shifted. + */ + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + bytes = grid->bytes >> 1; + scratch = grid->bitmap + bytes; + (void) memset((char *) scratch, 0, bytes); + + /* + * Shift just the glyph rectangle to keep things fast. + */ + wd += x; + ht += y; + for (dy = y; dy < ht; dy++) { + col = x * grid->bpp; + for (dx = x; dx < wd; dx++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = grid->bitmap[(dy * bpr) + (col >> 3)] & masks[si]; + if (byte) { + nx = dx + (xdir * xcount); + ny = dy + (ydir * ycount); + nx *= grid->bpp; + di = (nx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + scratch[(ny * bpr) + (nx >> 3)] |= byte; + } + } + } + + /* + * Copy the scratch buffer back to the main buffer. + */ + (void) memcpy((char *) grid->bitmap, (char *) scratch, bytes); + + /* + * Adjust the top-left coordinate of the glyph rectangle. + */ + grid->glyph_x += xdir * xcount; + grid->glyph_y += ydir * ycount; + + /* + * Adjust the glyph offsets relative to the baseline coordinates. + */ + grid->glyph_bbx.x_offset = grid->glyph_x - grid->base_x; + grid->glyph_bbx.y_offset = grid->base_y - + (grid->glyph_y + grid->glyph_bbx.height); + + /* + * Adjust the glyph ascent and descent. + */ + grid->glyph_bbx.ascent = grid->base_y - grid->glyph_y; + grid->glyph_bbx.descent = (grid->glyph_y + grid->glyph_bbx.height) - + grid->base_y; + + /* + * Mark the grid as being modified. + */ + grid->modified = 1; + + return 1; +} + + +int +#ifdef __STDC__ +bdf_grid_flip(bdf_glyph_grid_t *grid, short dir) +#else +bdf_grid_flip(grid, dir) +bdf_glyph_grid_t *grid; +short dir; +#endif +{ + int flipped, sel, delta; + short dx, dy, x, y, nx, ny, wd, ht; + unsigned short bytes, bpr, si, di, col, colx, byte; + unsigned char *bmap, *scratch, *masks; + + flipped = 0; + + if (grid == 0) + return flipped; + + if (grid->sel.width != 0 && grid->sel.height != 0) { + sel = 1; + x = y = 0; + wd = grid->sel.width; + ht = grid->sel.height; + bpr = ((wd * grid->bpp) + 7) >> 3; + bytes = grid->sel.bytes >> 1; + bmap = grid->sel.bitmap; + } else { + sel = 0; + x = grid->glyph_x; + y = grid->glyph_y; + wd = grid->glyph_bbx.width; + ht = grid->glyph_bbx.height; + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + bytes = grid->bytes >> 1; + bmap = grid->bitmap; + } + + /* + * If the width or height is 0, don't do anything. + */ + if (wd == 0|| ht == 0) + return flipped; + + nx = 0; + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; di = 7; break; + case 2: masks = twobpp; di = 3; break; + case 4: masks = fourbpp; di = 1; break; + } + + /* + * Set and initialize the scratch area. + */ + scratch = bmap + bytes; + (void) memset((char *) scratch, 0, bytes); + + wd += x; + ht += y; + + if (dir < 0) { + /* + * Flip horizontally. + */ + for (dy = y; dy < ht; dy++) { + col = x * grid->bpp; + for (nx = wd - 1, dx = x; dx < wd; dx++, nx--, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = bmap[(dy * bpr) + (col >> 3)] & masks[si]; + if (byte) { + flipped = 1; + colx = nx * grid->bpp; + di = (colx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + scratch[(dy * bpr) + (colx >> 3)] |= byte; + } + } + } + if (flipped) { + if (sel) + grid->sel.x += nx + 1; + else { + grid->glyph_x = nx + 1; + grid->glyph_bbx.x_offset = grid->glyph_x - grid->base_x; + } + } + } else { + /* + * Flip vertically. + */ + for (ny = ht - 1, dy = y; dy < ht; dy++, ny--) { + col = x * grid->bpp; + for (dx = x; dx < wd; dx++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = bmap[(dy * bpr) + (col >> 3)] & masks[si]; + if (byte) { + flipped = 1; + scratch[(ny * bpr) + (col >> 3)] |= byte; + } + } + } + if (flipped) { + if (sel) + grid->sel.y += ny + 1; + else { + grid->glyph_y = ny + 1; + grid->glyph_bbx.y_offset = grid->base_y - + (grid->glyph_y + grid->glyph_bbx.height); + grid->glyph_bbx.ascent = grid->base_y - grid->glyph_y; + grid->glyph_bbx.descent = + (grid->glyph_y + grid->glyph_bbx.height) - grid->base_y; + } + } + } + + if (flipped) { + /* + * Copy the scratch area back to the working area. + */ + if (sel) + (void) memcpy((char *) grid->sel.bitmap, (char *) scratch, bytes); + else + (void) memcpy((char *) grid->bitmap, (char *) scratch, bytes); + + if (sel) { + /* + * Check to see if flipping the selection caused the glyph metrics + * to change. + */ + if (_bdf_grid_ink_bounds(grid, &x, &y, &wd, &ht)) { + if (x < grid->glyph_x) { + delta = grid->glyph_x - x; + grid->glyph_bbx.width += delta; + grid->glyph_bbx.x_offset -= delta; + grid->glyph_x -= delta; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth += delta; + } else if (x >= grid->glyph_x + grid->glyph_bbx.width) { + delta = x - (grid->glyph_x + grid->glyph_bbx.width); + grid->glyph_bbx.width += delta; + if (grid->spacing == BDF_PROPORTIONAL) + grid->dwidth += delta; + } + + if (y < grid->glyph_y) { + delta = grid->glyph_y - y; + grid->glyph_bbx.height += delta; + grid->glyph_bbx.ascent += delta; + grid->glyph_y -= delta; + } else if (y >= grid->glyph_y + grid->glyph_bbx.height) { + delta = y - (grid->glyph_y + grid->glyph_bbx.height); + grid->glyph_bbx.height += delta; + grid->glyph_bbx.y_offset -= delta; + grid->glyph_bbx.descent += delta; + } + } + } + + /* + * Mark the grid as being modified. + */ + grid->modified = 1; + } + + return flipped; +} + +void +#ifdef __STDC__ +bdf_grid_origin(bdf_glyph_grid_t *grid, short *x, short *y) +#else +bdf_grid_origin(grid, x, y) +bdf_glyph_grid_t *grid; +short *x, *y; +#endif +{ + if (grid == 0) + return; + + *x = grid->base_x; + *y = grid->base_y; +} + +bdf_glyph_t * +#ifdef __STDC__ +bdf_grid_glyph(bdf_glyph_grid_t *grid) +#else +bdf_grid_glyph(grid) +bdf_glyph_grid_t *grid; +#endif +{ + int len; + short x, y, nx, ny, wd, ht, gx, gy; + unsigned short bpr, nbpr, si, di, col, byte; + bdf_glyph_t *glyph; + unsigned char *masks; + double ps, dw, rx; + + if (grid == 0) + return 0; + + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; di = 7; break; + case 2: masks = twobpp; di = 3; break; + case 4: masks = fourbpp; di = 1; break; + } + + /* + * Create the new glyph. + */ + glyph = (bdf_glyph_t *) malloc(sizeof(bdf_glyph_t)); + (void) memset((char *) glyph, 0, sizeof(bdf_glyph_t)); + + gx = grid->glyph_x; + gy = grid->glyph_y; + + /* + * Copy the bounding box. + */ + (void) memcpy((char *) &glyph->bbx, (char *) &grid->glyph_bbx, + sizeof(bdf_bbx_t)); + + /* + * If the font has character-cell spacing, then make sure the bitmap is + * cropped to fit within the bounds of the font bbx. + */ + if (grid->spacing == BDF_CHARCELL) { + if (gx < grid->base_x) { + glyph->bbx.x_offset = 0; + glyph->bbx.width -= grid->base_x - gx; + gx += grid->base_x - gx; + } + if (glyph->bbx.width > grid->font_bbx.width) + glyph->bbx.width -= glyph->bbx.width - grid->font_bbx.width; + } + + /* + * Set up its bitmap. + */ + nbpr = ((glyph->bbx.width * grid->bpp) + 7) >> 3; + glyph->bytes = nbpr * glyph->bbx.height; + glyph->bitmap = (unsigned char *) malloc(glyph->bytes); + (void) memset((char *) glyph->bitmap, 0, glyph->bytes); + + /* + * Set the other values. + */ + if (grid->name != 0) { + len = strlen(grid->name) + 1; + glyph->name = (char *) malloc(len); + (void) memcpy(glyph->name, grid->name, len); + } + glyph->encoding = grid->encoding; + glyph->dwidth = grid->dwidth; + + /* + * Reset the glyph SWIDTH value. + */ + ps = (double) grid->point_size; + rx = (double) grid->resolution_x; + dw = (double) grid->dwidth; + glyph->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx)); + + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + wd = gx + glyph->bbx.width; + ht = gy + glyph->bbx.height; + + /* + * Copy the bitmap from the grid into the glyph. + */ + for (ny = 0, y = gy; y < ht; y++, ny++) { + col = gx * grid->bpp; + for (nx = 0, x = gx; x < wd; x++, nx += grid->bpp, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = grid->bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + di = (nx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + glyph->bitmap[(ny * nbpr) + (nx >> 3)] |= byte; + } + } + } + + /* + * Return the new glyph. + */ + return glyph; +} + +/* + * Create a bitmap with the glyph image as well as the selection. + */ +void +#ifdef __STDC__ +bdf_grid_image(bdf_glyph_grid_t *grid, bdf_bitmap_t *image) +#else +bdf_grid_image(grid, image) +bdf_glyph_grid_t *grid; +bdf_bitmap_t *image; +#endif +{ + short x, y, ix, iy; + unsigned short bpr, ibpr, si, di, col, colx, byte; + unsigned char *masks; + + if (grid == 0 || image == 0) + return; + + masks = 0; + switch (grid->bpp) { + case 1: masks = onebpp; di = 7; break; + case 2: masks = twobpp; di = 3; break; + case 4: masks = fourbpp; di = 1; break; + } + + image->bpp = grid->bpp; + image->x = image->y = 0; + image->width = grid->grid_width; + image->height = grid->grid_height; + image->bytes = grid->bytes >> 1; + image->bitmap = (unsigned char *) malloc(image->bytes); + (void) memcpy((char *) image->bitmap, (char *) grid->bitmap, image->bytes); + + /* + * Add the selection to the bitmap if it exists. + */ + if (grid->sel.width != 0 && grid->sel.height != 0) { + ibpr = ((image->width * grid->bpp) + 7) >> 3; + bpr = ((grid->sel.width * grid->bpp) + 7) >> 3; + for (iy = grid->sel.y, y = 0; y < grid->sel.height; y++, iy++) { + for (ix = grid->sel.x, col = x = 0; x < grid->sel.width; + x++, ix++, col += grid->bpp) { + si = (col & 7) / grid->bpp; + byte = grid->sel.bitmap[(y * bpr) + (col >> 3)] & masks[si]; + if (byte) { + colx = ix * grid->bpp; + di = (colx & 7) / grid->bpp; + if (di < si) + byte <<= (si - di) * grid->bpp; + else if (di > si) + byte >>= (di - si) * grid->bpp; + image->bitmap[(iy * ibpr) + (colx >> 3)] |= byte; + } + } + } + } +} + +/* + * Routines for quick and dirty dithering. + */ +static void +#ifdef __STDC__ +_bdf_one_to_n(bdf_bitmap_t *bmap, int n) +#else +_bdf_one_to_n(bmap, n) +bdf_bitmap_t *bmap; +int n; +#endif +{ + unsigned short bpr, sbpr, bytes, col, sx, sy; + unsigned char *nbmap, *masks; + + if (bmap == 0 || bmap->width == 0 || bmap->height == 0) + return; + + masks = 0; + switch (n) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + sbpr = (bmap->width + 7) >> 3; + bpr = ((bmap->width * n) + 7) >> 3; + bytes = bpr * bmap->height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < bmap->height; sy++) { + for (col = sx = 0; sx < bmap->width; sx++, col += n) { + if (bmap->bitmap[(sy * sbpr) + (sx >> 3)] & (0x80 >> (sx & 7))) + nbmap[(sy * bpr) + (col >> 3)] |= masks[(col & 7) / n]; + } + } + free((char *) bmap->bitmap); + bmap->bpp = n; + bmap->bytes = bytes; + bmap->bitmap = nbmap; +} + +static void +#ifdef __STDC__ +_bdf_n_to_one(bdf_bitmap_t *bmap) +#else +_bdf_n_to_one(bmap) +bdf_bitmap_t *bmap; +#endif +{ + unsigned short bpr, sbpr, bytes, col, sx, sy; + unsigned char *nbmap, *masks; + + if (bmap == 0 || bmap->width == 0 || bmap->height == 0) + return; + + masks = 0; + switch (bmap->bpp) { + case 1: masks = onebpp; break; + case 2: masks = twobpp; break; + case 4: masks = fourbpp; break; + } + + sbpr = ((bmap->width * bmap->bpp) + 7) >> 3; + bpr = (bmap->width + 7) >> 3; + bytes = bpr * bmap->height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < bmap->height; sy++) { + for (col = sx = 0; sx < bmap->width; sx++, col += bmap->bpp) { + if (bmap->bitmap[(sy * sbpr) + (col >> 3)] & + masks[(col & 7) / bmap->bpp]) + nbmap[(sy * bpr) + (sx >> 3)] |= (0x80 >> (sx & 7)); + } + } + free((char *) bmap->bitmap); + bmap->bpp = 1; + bmap->bytes = bytes; + bmap->bitmap = nbmap; +} + +static void +#ifdef __STDC__ +_bdf_two_to_four(bdf_bitmap_t *bmap) +#else +_bdf_two_to_four(bmap) +bdf_bitmap_t *bmap; +#endif +{ + unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy; + unsigned char *nbmap, *masks; + + if (bmap == 0 || bmap->width == 0 || bmap->height == 0) + return; + + masks = twobpp; + + sbpr = ((bmap->width << 1) + 7) >> 3; + bpr = ((bmap->width << 2) + 7) >> 3; + bytes = bpr * bmap->height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < bmap->height; sy++) { + for (col = sx = 0; sx < bmap->width; sx++, col += 2) { + si = (col & 7) >> 1; + byte = bmap->bitmap[(sy * sbpr) + (col >> 3)] & masks[si]; + if (byte) { + /* + * Shift the byte down to make an index. + */ + if (si < 3) + byte >>= (3 - si) << 1; + + /* + * Scale the index to four bits per pixel and shift it into + * place before adding it. + */ + byte = (byte << 2) + 3; + if ((sx & 1) == 0) + byte <<= 4; + nbmap[(sy * bpr) + ((sx << 2) >> 3)] |= byte; + } + } + } + free((char *) bmap->bitmap); + bmap->bpp = 4; + bmap->bytes = bytes; + bmap->bitmap = nbmap; +} + +static void +#ifdef __STDC__ +_bdf_four_to_two(bdf_bitmap_t *bmap) +#else +_bdf_four_to_two(bmap) +bdf_bitmap_t *bmap; +#endif +{ + unsigned short bpr, sbpr, bytes, col, si, byte, sx, sy; + unsigned char *nbmap, *masks; + + if (bmap == 0 || bmap->width == 0 || bmap->height == 0) + return; + + masks = fourbpp; + + sbpr = ((bmap->width << 2) + 7) >> 3; + bpr = ((bmap->width << 1) + 7) >> 3; + bytes = bpr * bmap->height; + nbmap = (unsigned char *) malloc(bytes); + (void) memset((char *) nbmap, 0, bytes); + + for (sy = 0; sy < bmap->height; sy++) { + for (col = sx = 0; sx < bmap->width; sx++, col += 4) { + si = (col & 7) >> 2; + byte = bmap->bitmap[(sy * sbpr) + (col >> 3)] & masks[si]; + if (byte) { + /* + * Shift the byte down to make an index. + */ + if (si == 0) + byte >>= 4; + + /* + * Scale the index to two bits per pixel and shift it into + * place if necessary. + */ + byte >>= 2; + + si = ((sx << 1) & 7) >> 1; + if (si < 3) + byte <<= (3 - si) << 1; + + nbmap[(sy * bpr) + ((sx << 1) >> 3)] |= byte; + } + } + } + free((char *) bmap->bitmap); + bmap->bpp = 2; + bmap->bytes = bytes; + bmap->bitmap = nbmap; +} + +/* + * Add a bitmap to a grid as a selection. + */ +void +#ifdef __STDC__ +bdf_add_selection(bdf_glyph_grid_t *grid, bdf_bitmap_t *sel) +#else +bdf_add_selection(grid, sel) +bdf_glyph_grid_t *grid; +bdf_bitmap_t *sel; +#endif +{ + unsigned short bytes, bpr; + + if (grid == 0 || sel == 0 || sel->width == 0 || sel->height == 0 || + sel->bytes == 0) + return; + + if (sel->bpp != grid->bpp) { + /* + * Dither the incoming bitmap to match the same bits per pixel as the + * grid it is being added to. + */ + if (sel->bpp == 1) + _bdf_one_to_n(sel, grid->bpp); + else if (grid->bpp == 1) + _bdf_n_to_one(sel); + else if (sel->bpp == 2) + _bdf_two_to_four(sel); + else + _bdf_four_to_two(sel); + } + + /* + * If the bitmap is too big then trim the right and/or the bottom to fit + * in the grid. + */ + if (sel->width > grid->grid_width) + sel->width = grid->grid_width; + if (sel->height > grid->grid_height) + sel->height = grid->grid_height; + + /* + * If the positioning puts the selection bitmap off one of the edges, + * adjust it so it is completely on the grid. + */ + if (sel->x + sel->width > grid->grid_width) + sel->x -= (sel->x + sel->width) - grid->grid_width; + if (sel->y + sel->height > grid->grid_height) + sel->y -= (sel->y + sel->height) - grid->grid_height; + + bpr = ((sel->width * grid->bpp) + 7) >> 3; + bytes = (bpr * sel->height) << 1; + + /* + * Resize the storage for the selection bitmap if necessary. + */ + if (bytes > grid->sel.bytes) { + if (grid->sel.bytes == 0) + grid->sel.bitmap = (unsigned char *) malloc(bytes); + else + grid->sel.bitmap = (unsigned char *) + realloc((char *) grid->sel.bitmap, bytes); + grid->sel.bytes = bytes; + } + + /* + * Copy the width and height values. + */ + grid->sel.x = sel->x; + grid->sel.y = sel->y; + grid->sel.width = sel->width; + grid->sel.height = sel->height; + + /* + * Copy the incoming bitmap to the new selection bitmap. + */ + (void) memcpy((char *) grid->sel.bitmap, (char *) sel->bitmap, + bytes >> 1); + + /* + * Crop the image to adjust the glyph bounding box. + */ + (void) bdf_grid_crop(grid, 1); +} + +int +#ifdef __STDC__ +bdf_grid_color_at(bdf_glyph_grid_t *grid, short x, short y) +#else +bdf_grid_color_at(grid, x, y) +bdf_glyph_grid_t *grid; +short x, y; +#endif +{ + unsigned short bpr, si, di, byte; + unsigned char *masks; + + if (grid->bpp == 1) + return -1; + + masks = twobpp; + di = 0; + switch (grid->bpp) { + case 2: di = 3; break; + case 4: di = 1; break; + } + + x *= grid->bpp; + + bpr = ((grid->grid_width * grid->bpp) + 7) >> 3; + si = (x & 7) / grid->bpp; + + byte = grid->bitmap[(y * bpr) + (x >> 3)] & masks[si]; + if (di > si) + byte >>= (di - si) * grid->bpp; + return (int) byte; +} diff --git a/engines/sci/tools/bdftofont.c b/engines/sci/tools/bdftofont.c new file mode 100644 index 0000000000..4e7d9ddb65 --- /dev/null +++ b/engines/sci/tools/bdftofont.c @@ -0,0 +1,181 @@ +/*************************************************************************** + bdftofont.c Copyright (C) 2003 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include +#include +#include + + +bdf_options_t bdf_opts = { + 0, /* ttf_hint */ + 0, /* correct_metrics */ + 0, /* keep_unencoded */ + 0, /* keep_comments */ + 0, /* pad_cells */ + 0, /* font_spacing */ + 0, /* point_size */ + 0, /* resolution_x */ + 0, /* resolution_y */ + 0, /* bits_per_pixel */ + BDF_UNIX_EOL /* eol */ +}; + + +#define GLYPH(n) glyphs[(((n) > 31)? ((n) - 31) : 0)] + +void +convert_font(FILE *in_file, FILE *out_file) +{ + bdf_font_t *font = + bdf_load_font(in_file, + &bdf_opts, + NULL, NULL); + + int chars_nr = font->glyphs_used; + int width = font->monowidth; + int bytes_per_row = (width + 7) >> 3; + int line_height; + int char_height = 0; + int char_byte_size; + int i; + bdf_glyph_t *glyphs = font->glyphs; + + fclose(in_file); + + if (!font) { + fprintf(stderr, "Not a BDF file? Aborting!\n"); + exit(1); + } + + printf("Res = %d/%d; pointsize = %d, spacing = %d, width=%d, asc=%ld, desc=%ld, glyphs=%ld\n", + font->resolution_x, + font->resolution_y, + font->point_size, + font->spacing, + font->monowidth, + font->font_ascent, + font->font_descent, + chars_nr); + + if (chars_nr > 256) + chars_nr = 256; + + for (i = 0; i < chars_nr; i++) { + int rh = GLYPH(i).bbx.height; + + if (rh > char_height) + char_height = rh; + } + + + + line_height = char_height + 1; + char_byte_size = bytes_per_row * char_height; + + fprintf(out_file, "# %d %d\n", chars_nr, char_height + 1); + + for (i = 0; i < chars_nr; i++) { + int rh = GLYPH(i).bbx.height; + int rw = GLYPH(i).bbx.width; + int xoff = 0; /* GLYPH(i).bbx.x_offset; */ + int yoff = 0; /* GLYPH(i).bbx.y_offset; */ + int j, k; + int top_pad = yoff; + int bot_pad = (char_height - rh) - top_pad; + int bytes_to_read = (GLYPH(i).bytes) / rh; + unsigned char *data = GLYPH(i).bitmap; + + if (bytes_to_read <= 0) { + fprintf(stderr, "No bytes per row: bytes=%d, w=%d, h=%d\n", + GLYPH(i).bytes, rw, rh); + exit(1); + } + + if (bot_pad < 0) { + fprintf(stderr, "Bottom padding <0: height=%d/%d, top_pad=%d\n", + rh, char_height, yoff); + exit(1); + } + + /* First, pad everything */ + for (j = 0; j < top_pad; j++) { + for (k = 0; k < rw; k++) + fprintf(out_file, "."); + fprintf(out_file, "\n"); + } + + /* Write char data */ + for (j = 0; j < rh; j++) { + unsigned int b = 0; + unsigned int oldb; + for (k = 0; k < bytes_to_read; k++) { + int shift_offset = 8 * (bytes_to_read - 1 - k); + b |= (*data++ << shift_offset); + } + + oldb = b; + + for (k = 0; k < rw; k++) + fprintf(out_file, (oldb & (1 << ((bytes_per_row * 8) - 1 - k)))? "#":"."); + + fprintf(out_file, "\n"); + } + + /* Pad bottom */ + for (j = 0; j < bot_pad; j++) { + for (k = 0; k < rw; k++) + fprintf(out_file, "."); + fprintf(out_file, "\n"); + } + fprintf(out_file,"----\t 0x%02x ('%c') */\n", i, ((i>31)&&(i<0x7f))?i:'.'); + } + fprintf(out_file, "\n\n"); + + fclose(out_file); +} + + +int +main(int argc, char **argv) +{ + FILE *f = NULL; + bdf_setup(); + + if (argc < 3) { + fprintf(stderr, "Usage: %s \n ", argv[0]); + exit(1); + } + + f = fopen(argv[1], "r"); + if (f) + convert_font(f, fopen(argv[2], "w")); + else + perror(argv[1]); + + bdf_cleanup(); +} diff --git a/engines/sci/tools/classes.c b/engines/sci/tools/classes.c new file mode 100644 index 0000000000..cdb8c93f9f --- /dev/null +++ b/engines/sci/tools/classes.c @@ -0,0 +1,54 @@ +#include +#include + +#include +#include + +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(ilength-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 + +***************************************************************************/ + +#include +#include +#include +#include + + +#define GLYPH(n) glyphs[n] + + +int max_width = 0; + +char inbuf[256]; + +typedef struct { + int bytes; + int width; + unsigned char *bitmap; +} glyph_t; + + +unsigned int +invert_bits(unsigned int s, int bits) +{ + unsigned int rv = 0; + int i; + for (i = 0; i < bits; i++) + if (s & (1 << i)) + rv |= (1 << (bits - 1 - i)); + + return rv; +} + +void +read_single_glyph(FILE *in_file, glyph_t *dest, int index, int char_height) +{ + int width = -1; + int bytes = 0; + int i; + unsigned char *data + = dest->bitmap = (unsigned char *) malloc(char_height * 4); /* Let's waste memory */ + + do { + unsigned int d = 0; + + fgets(inbuf, 255, in_file); + + if (char_height-- == -1) { + fprintf(stderr, "Char 0x%02x is too long!\n", index); + exit(1); + } + + if (inbuf[0] == '.' || inbuf[0] == '#') { + int cw; + + if (width == -1) + width = strlen(inbuf) - 1; + else if (strlen(inbuf) - 1 != width) { + fprintf(stderr, "Char 0x%02x uses inconsistent width\n", index); + exit(1); + } + + for (i = 0; i < width; i++) + if (inbuf[i] == '#') + d |= (1 << i); + + d = invert_bits(d, ((width + 7) / 8) * 8); + + cw = width; + do { + *data = d & 0xff; + d >>= 8; + data++; + cw -= 8; + ++bytes; + } while (cw > 0); + } + + } while (inbuf[0] == '.' || inbuf[0] == '#'); + + if (char_height >= 0) { + fprintf(stderr, "Char 0x%02x is too short (by %d)!\n", index, char_height); + exit(1); + } + + dest->width = width; + if (width > max_width) + max_width = width; + + dest->bytes = bytes; +} + +glyph_t * +read_glyphs(FILE *in_file, int nr, int char_height) +{ + int i; + glyph_t *glyphs = (glyph_t *) calloc(sizeof(glyph_t), nr); + + for (i = 0; i < nr; i++) + read_single_glyph(in_file, glyphs + i, i, char_height); + + return glyphs; +} + +void +convert_font(FILE *in_file, char *structname, FILE *out_file) +{ + int chars_nr; + glyph_t *glyphs; + int bytes_per_row; + int line_height; + int char_height = 0; + int char_byte_size; + int i; + + fscanf(in_file, "# %d %d\n", &chars_nr, &char_height); + printf("Parsing %d characters at height %d\n", chars_nr, char_height); + glyphs = read_glyphs(in_file, chars_nr, char_height); + + bytes_per_row = (max_width + 7) >> 3; + line_height = char_height + 1; + fclose(in_file); + + char_byte_size = bytes_per_row * char_height; + + fprintf(out_file, "/* Auto-generated by bdftofont.c */\n\n"); + + fprintf(out_file, "#include \n\n"); + + fprintf(out_file, "static int %s_widths[] = {\n", structname); + for (i = 0; i < chars_nr; i++) { + int rw = GLYPH(i).width; + fprintf(out_file, "\t%d%s\t/* 0x%02x */\n", rw, + (i < (chars_nr-1))? ",":"", i); + } + fprintf(out_file, "};\n\n"); + + fprintf(out_file, "static unsigned char %s_data[] = {\n", structname); + for (i = 0; i < chars_nr; i++) { + int rh = char_height; + int rw = GLYPH(i).width; + int xoff = 0; + int yoff = 0; + int j, k; + + int top_pad = yoff; + int bot_pad = (char_height - rh) - top_pad; + int bytes_to_read = (GLYPH(i).bytes) / rh; + unsigned char *data = GLYPH(i).bitmap; + + if (bytes_to_read <= 0) { + fprintf(stderr, "No bytes per row: bytes=%d, w=%d, h=%d\n", + GLYPH(i).bytes, rw, rh); + exit(1); + } + + if (bot_pad < 0) { + fprintf(stderr, "Bottom padding <0: height=%d/%d, top_pad=%d\n", + rh, char_height, yoff); + exit(1); + } + + fprintf(out_file,"\t/* 0x%02x ('%c') */\n", i, ((i>31)&&(i<0x7f))?i:'.'); + /* First, pad everything */ + for (j = 0; j < top_pad; j++) { + fprintf(out_file, "\t"); + for (k = 0; k < bytes_per_row; k++) + fprintf(out_file, "0x00, "); + fprintf(out_file, "\n"); + } + + /* Write char data */ + for (j = 0; j < rh; j++) { + unsigned int b = 0; + unsigned int oldb; + fprintf(out_file, "\t"); + for (k = 0; k < bytes_to_read; k++) { + int shift_offset = 8 * (bytes_to_read - 1 - k); + b |= (*data++ << shift_offset); + } + + oldb = b; + + for (k = 0; k < bytes_per_row; k++) { + fprintf(out_file, "0x%02x%s", (b & 0xff), (bot_pad || (i+1 < chars_nr) || (j+1 < rh) || (k+1 < bytes_per_row))?", ":""); + b >>= 8; + } + fprintf(out_file, "\t/* "); + for (k = 0; k < rw; k++) + fprintf(out_file, (oldb & (1 << ((bytes_per_row * 8) - 1 - k)))? "##":".."); + fprintf(out_file, " */"); + + fprintf(out_file, "\n"); + } + + /* Pad bottom */ + for (j = 0; j < bot_pad; j++) { + fprintf(out_file, "\t"); + for (k = 0; k < bytes_per_row; k++) + fprintf(out_file, "0x00%s", ((i+1 < chars_nr) || (j+1 < bot_pad) || (k+1 < bytes_per_row))?", ":""); + fprintf(out_file, "\n"); + } + } + fprintf(out_file, "};\n\n"); + + fprintf(out_file, "gfx_bitmap_font_t %s = {\n", structname); + fprintf(out_file, "\t-1, /* resource ID */\n"); + fprintf(out_file, "\t%d, /* # of characters */\n", chars_nr); + fprintf(out_file, "\t%s_widths, /* Widths */\n", structname); + fprintf(out_file, "\t%d, /* Bytes per row */\n", bytes_per_row); + fprintf(out_file, "\t%d, /* Line height */\n", line_height); + fprintf(out_file, "\t%d, /* Char height */\n", char_height); + fprintf(out_file, "\t%d, /* Char size (occupied, in bytes) */\n", char_byte_size); + fprintf(out_file, "\t%s_data /* Bulk data */\n", structname); + fprintf(out_file, "};\n"); + + fclose(out_file); +} + + +int +main(int argc, char **argv) +{ + FILE *f = NULL; + + if (argc < 4) { + fprintf(stderr, "Usage: %s \n ", argv[0]); + exit(1); + } + + f = fopen(argv[1], "r"); + if (f) + convert_font(f, argv[2], fopen(argv[3], "w")); + else + perror(argv[1]); + +} diff --git a/engines/sci/tools/graphics_png.h b/engines/sci/tools/graphics_png.h new file mode 100644 index 0000000000..d0dcd4bb9e --- /dev/null +++ b/engines/sci/tools/graphics_png.h @@ -0,0 +1,113 @@ +/*************************************************************************** + graphics_png.c Copyright (C) 1999 Christoph Reichenbach, TU Darmstadt + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + +***************************************************************************/ +/* 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 +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_LIBPNG + +#include +#include +#include + +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/engines/sci/tools/listwords.c b/engines/sci/tools/listwords.c new file mode 100644 index 0000000000..8e63aada37 --- /dev/null +++ b/engines/sci/tools/listwords.c @@ -0,0 +1,91 @@ +/*************************************************************************** + listwords.c Copyright (C) 2000 Christoph Reichenbach, TU Darmstadt + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + + History: + + 000413 - created (LS) from vocabdump.c + +***************************************************************************/ + +#include "sciunpack.h" +#include +#include + +int +_vocab_cmp_group(const void *word1, const void *word2) +{ +#define fw (* ((word_t **) word1)) +#define sw (* ((word_t **) word2)) + if (fw->groupgroup) + 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<w_class) + printf("|"); else + printf("\n"); + } + tracker++; + } + + vocab_free_words(words, words_nr); + + return 0; +} + + + + + + diff --git a/engines/sci/tools/makefile.dos b/engines/sci/tools/makefile.dos new file mode 100644 index 0000000000..a8adc59e26 --- /dev/null +++ b/engines/sci/tools/makefile.dos @@ -0,0 +1,38 @@ +# +# FreeSCI/DOS Makefile +# +# 19991221 rink created this file +# +# +TARGET : utils + +FILES = main.o config.o yywrap.o +LIBS = ../core/scicore.a ../sound/scisound.a ../graphics/scigraphics.a + +CC = gcc +CFLAGS = -g -c -I../include -I../.. -D_DOS -DHAVE_LIBPNG -DHAVE_DGFX -DHAVE_UNISTD_H + +clean: + del *.o *.exe + +utils: scidisasm.exe sciconsole.exe sciunpack.exe scriptdump.exe \ + vocabdump.exe classes.exe + + +scidisasm.exe: scidisasm.o + gcc -g -o scidisasm.exe scidisasm.o ${LIBS} -lpng -lz + +sciconsole.exe: sciconsole.o + gcc -g -o sciconsole.exe sciconsole.o ${LIBS} -lpng -lz + +sciunpack.exe: sciunpack.o + gcc -g -o sciunpack.exe sciunpack.o ${LIBS} -lpng -lz + +scriptdump.exe: scriptdump.o + gcc -g -o scriptdump.exe scriptdump.o ${LIBS} -lpng -lz + +vocabdump.exe: vocabdump.o + gcc -g -o vocabdump.exe vocabdump.o ${LIBS} -lpng -lz + +classes.exe: classes.o + gcc -g -o classes.exe classes.o ${LIBS} -lpng -lz diff --git a/engines/sci/tools/musicplayer.c b/engines/sci/tools/musicplayer.c new file mode 100644 index 0000000000..34024ddf15 --- /dev/null +++ b/engines/sci/tools/musicplayer.c @@ -0,0 +1,115 @@ +/*************************************************************************** + musicplayer.c Copyright (C) 2007 Lars Skovlund + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Lars Skovlund (LS) [lskovlun@image.dk] + + History: + + 071027 - created (LS) + +***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include + +#define DUMMY_SOUND_HANDLE 0xdeadbeef + +static song_iterator_t * +build_iterator(resource_mgr_t *resmgr, int song_nr, int type, songit_id_t id) +{ + resource_t *song = scir_find_resource(resmgr, sci_sound, song_nr, 0); + + if (!song) + return NULL; + + return songit_new(song->data, song->size, type, id); +} + +int main(int argc, char** argv) +{ + resource_mgr_t *resmgr; + sfx_state_t sound; + int res_version = SCI_VERSION_AUTODETECT; + int sound_nr; + song_handle_t dummy1; + int dummy2; + int arg; + int it_type = SCI_SONG_ITERATOR_TYPE_SCI0; + song_iterator_t *base, *ff; + + printf("FreeSCI %s music player Copyright (C) 1999-2007\n", VERSION); + printf(" Dmitry Jemerov, Christopher T. Lansdown, Sergey Lapin, Rickard Lind,\n" + " Carl Muckenhoupt, Christoph Reichenbach, Magnus Reftel, Lars Skovlund,\n" + " Rink Springer, Petr Vyhnak, Solomon Peachy, Matt Hargett, Alex Angas\n" + " Walter van Niftrik, Rainer Canavan, Ruediger Hanke, Hugues Valois\n" + "This program is free software. You can copy and/or modify it freely\n" + "according to the terms of the GNU general public license, v2.0\n" + "or any later version, at your option.\n" + "It comes with ABSOLUTELY NO WARRANTY.\n"); + if (argc < 3) + { + fprintf(stderr,"Syntax: %s [ ...]\n", argv[0]); + return 1; + } + + if (!(resmgr = scir_new_resource_manager(argv[1], res_version, + 0, 1024*128))) { + fprintf(stderr,"Could not find any resources; quitting.\n"); + return 2; + } + + if (resmgr->sci_version >= SCI_VERSION_01) + it_type = SCI_SONG_ITERATOR_TYPE_SCI1; + + sfx_init(&sound, resmgr, 0); + sfx_set_volume(&sound, 127); + + arg = 2 - 1; + while (++arg < argc) + { + sound_nr = atoi(argv[arg]); + base = ff = build_iterator(resmgr, sound_nr, it_type, + DUMMY_SOUND_HANDLE); + printf("Playing resource %d...\n", sound_nr); + if (sfx_add_song(&sound, ff, + 0, DUMMY_SOUND_HANDLE, sound_nr)) + { + fprintf(stderr, "Could not start sound resource. Does it exist?\n"); + return 2; + } + sfx_song_set_status(&sound, DUMMY_SOUND_HANDLE, SOUND_STATUS_PLAYING); + while (sfx_poll(&sound, &dummy1, &dummy2) != SI_FINISHED) + {}; + } + sfx_exit(&sound); + scir_free_resource_manager(resmgr); + return 0; +} + + diff --git a/engines/sci/tools/scidisasm.c b/engines/sci/tools/scidisasm.c new file mode 100644 index 0000000000..dc5e9f8d07 --- /dev/null +++ b/engines/sci/tools/scidisasm.c @@ -0,0 +1,1009 @@ +/*************************************************************************** + scidisasm.c Copyright (C) 1999 Christoph Reichenbach, TU Darmstadt + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Dmitry Jemerov (DJ) [yole@spb.cityline.ru] + + History: + + 991213 - created (DJ) + +***************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#define MALLOC_DEBUG + +#include +#include +#include +#include + +#ifdef HAVE_GETOPT_H +#include +#endif /* HAVE_GETOPT_H */ + +static int hexdump = 0; +static int opcode_size = 0; +static int verbose = 0; + +static resource_mgr_t *resmgr; + +#ifdef HAVE_GETOPT_LONG +static struct option options[] = { + {"version", no_argument, 0, 256}, + {"help", no_argument, 0, 'h'}, + {"hexdump", no_argument, &hexdump, 1}, + {"opcode-size", no_argument, &opcode_size, 1}, + {"verbose", no_argument, &verbose, 1}, + {"gamedir", required_argument, 0, 'd'}, + {0, 0, 0, 0}}; +#endif /* HAVE_GETOPT_LONG */ + +#define SCI_ASSUME_VERSION SCI_VERSION_FTU_NEW_SCRIPT_HEADER + +typedef struct name_s { + int offset; + char *name; + int class_no; + struct name_s *next; +} name_t; + +typedef struct area_s { + int start_offset; + int end_offset; + void *data; + struct area_s *next; +} area_t; + +enum area_type { area_said, area_string, area_object, area_last }; + +typedef struct script_state_s { + int script_no; + name_t *names; + area_t *areas [area_last]; + + struct script_state_s *next; +} script_state_t; + +typedef struct disasm_state_s { + char **snames; + int selector_count; + opcode *opcodes; + int kernel_names_nr; + char **kernel_names; + word_t **words; + int word_count; + + char **class_names; + int *class_selector_count; + short **class_selectors; + int class_count; + int old_header; + + script_state_t *scripts; +} disasm_state_t; + +void +disassemble_script(disasm_state_t *d, int res_no, int pass_no); + +script_state_t * +find_script_state (disasm_state_t *d, int script_no); + +void +script_free_names (script_state_t *s); + +void +script_add_name (script_state_t *s, int aoffset, char *aname, int aclass_no); + +char * +script_find_name (script_state_t *s, int offset, int *class_no); + +void +script_add_area (script_state_t *s, int start_offset, int end_offset, int type, void *data); + +void +script_free_areas (script_state_t *s); + +int +script_get_area_type (script_state_t *s, int offset, void **pdata); + +void +disasm_init (disasm_state_t *d); + +void +disasm_free_state (disasm_state_t *d); + +int main(int argc, char** argv) +{ + int i; + char outfilename [256]; + int optindex = 0; + int c; + disasm_state_t disasm_state; + char *gamedir = NULL; + int res_version = SCI_VERSION_AUTODETECT; + +#ifdef HAVE_GETOPT_LONG + while ((c = getopt_long(argc, argv, "vhxr:d:", options, &optindex)) > -1) { +#else /* !HAVE_GETOPT_H */ + while ((c = getopt(argc, argv, "vhxr:d:")) > -1) { +#endif /* !HAVE_GETOPT_H */ + + switch (c) + { + case 256: + printf("scidisasm ("PACKAGE") "VERSION"\n"); + printf("This program is copyright (C) 1999 Christoph Reichenbach.\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': + printf("Usage: scidisasm\n" + "\nAvailable options:\n" + " --version Prints the version number\n" + " --help -h Displays this help message\n" + " --gamedir -d Read game resources from dir\n" + " --hexdump -x Hex dump all script resources\n" + " --verbose Print additional disassembly information\n" + " --opcode-size Print opcode size postfixes\n"); + exit(0); + + case 'd': + if (gamedir) sci_free (gamedir); + gamedir= sci_strdup (optarg); + break; + + case 'r': + res_version = atoi(optarg); + break; + + case 0: /* getopt_long already did this for us */ + case '?': + /* getopt_long already printed an error message. */ + break; + + default: + return -1; + } + } + + if (gamedir) + if (chdir (gamedir)) + { + printf ("Error changing to game directory '%s'\n", gamedir); + exit(1); + } + + printf ("Loading resources...\n"); + if (!(resmgr = scir_new_resource_manager(sci_getcwd(), res_version, + 1, 1024*128))) { + fprintf(stderr,"Could not find any resources; quitting.\n"); + exit(1); + } + + disasm_init (&disasm_state); + + script_adjust_opcode_formats(resmgr->sci_version); + + printf ("Performing first pass...\n"); + for (i=0; i < resmgr->resources_nr; i++) + if (resmgr->resources[i].type == sci_script) + disassemble_script(&disasm_state, + resmgr->resources[i].number, 1); + + printf ("Performing second pass...\n"); + for (i=0; i < resmgr->resources_nr; i++) + if (resmgr->resources[i].type == sci_script) + { + sprintf (outfilename, "%03d.script", + resmgr->resources[i].number); + open_console_file (outfilename); + disassemble_script(&disasm_state, + resmgr->resources[i].number, 2); + } + + close_console_file(); + disasm_free_state (&disasm_state); + + free(resmgr->resource_path); + scir_free_resource_manager(resmgr); + return 0; +} + +/* -- General operations on disasm_state_t ------------------------------- */ + +void +disasm_init (disasm_state_t *d) +{ + d->snames = vocabulary_get_snames (resmgr, &d->selector_count, SCI_ASSUME_VERSION); + d->opcodes = vocabulary_get_opcodes(resmgr); + d->kernel_names = vocabulary_get_knames (resmgr, &d->kernel_names_nr); + d->words = vocab_get_words (resmgr, &d->word_count); + d->scripts = NULL; + d->old_header = 0; + + d->class_count = vocabulary_get_class_count(resmgr); + d->class_names = (char **) sci_malloc (d->class_count * sizeof (char *)); + memset (d->class_names, 0, d->class_count * sizeof (char *)); + d->class_selector_count = (int *) sci_malloc (d->class_count * sizeof (int)); + memset (d->class_selector_count, 0, d->class_count * sizeof (int)); + d->class_selectors = (short **) sci_malloc (d->class_count * sizeof (short *)); + memset (d->class_selectors, 0, d->class_count * sizeof (short *)); +} + +void +disasm_free_state (disasm_state_t *d) +{ + script_state_t *s, *next_script; + int i; + + s=d->scripts; + while (s) { + next_script=s->next; + script_free_names (s); + script_free_areas (s); + s=next_script; + } + + for (i=0; iclass_count; i++) { + if (d->class_names [i]) sci_free (d->class_names [i]); + if (d->class_selectors [i]) sci_free (d->class_selectors [i]); + } + + free (d->class_names); + free (d->class_selectors); + free (d->class_selector_count); + + vocabulary_free_snames (d->snames); + vocabulary_free_opcodes (d->opcodes); + vocabulary_free_knames (d->kernel_names); + vocab_free_words (d->words, d->word_count); +} + +script_state_t * +find_script_state (disasm_state_t *d, int script_no) +{ + script_state_t *s; + + for (s=d->scripts; s; s=s->next) + if (s->script_no == script_no) return s; + + s=(script_state_t *) sci_malloc (sizeof (script_state_t)); + memset (s, 0, sizeof (script_state_t)); + s->script_no = script_no; + s->next = d->scripts; + + d->scripts=s; + return s; +} + +/* -- Name table operations ---------------------------------------------- */ + +void +script_free_names (script_state_t *s) +{ + name_t *p=s->names, *next_name; + + while (p) { + next_name=p->next; + free (p->name); + free (p); + p=next_name; + } + + s->names = NULL; +} + +void +script_add_name (script_state_t *s, int aoffset, char *aname, int aclass_no) +{ + name_t *p; + char *name=script_find_name (s, aoffset, NULL); + if (name) return; + + p=(name_t *) sci_malloc (sizeof (name_t)); + p->offset=aoffset; + p->name= sci_strdup (aname); + p->class_no=aclass_no; + p->next=s->names; + s->names=p; +} + +char * +script_find_name (script_state_t *s, int offset, int *aclass_no) +{ + name_t *p; + + for (p=s->names; p; p=p->next) + if (p->offset == offset) { + if (aclass_no && p->class_no != -2) *aclass_no = p->class_no; + return p->name; + } + + return NULL; +} + +/* -- Area table operations ---------------------------------------------- */ + +void +script_add_area (script_state_t *s, int start_offset, int end_offset, int type, void *data) +{ + area_t *area; + + area=(area_t *) sci_malloc (sizeof (area_t)); + area->start_offset = start_offset; + area->end_offset = end_offset; + area->data = data; + area->next = s->areas [type]; + + s->areas [type] = area; +} + +void +script_free_areas (script_state_t *s) +{ + int i; + + for (i=0; iareas [i], *next_area; + while (area) { + next_area=area->next; + free (area); + area=next_area; + } + } +} + +int +script_get_area_type (script_state_t *s, int offset, void **pdata) +{ + int i; + + for (i=0; iareas [i]; + while (area) { + if (area->start_offset <= offset && area->end_offset >= offset) { + if (pdata != NULL) *pdata=area->data; + return i; + } + area=area->next; + } + } + + return -1; +} + +char * +get_selector_name (disasm_state_t *d, int selector) +{ + static char selector_name [256]; + + if (d->snames && selector >= 0 && selector < d->selector_count) + return d->snames [selector]; + else { + sprintf (selector_name, "unknown_sel_%X", selector); + return selector_name; + } +} + +const char * +get_class_name (disasm_state_t *d, int class_no) +{ + static char class_name [256]; + + if (class_no == -1) + return ""; + else if (class_no >= 0 && class_no < d->class_count && d->class_names [class_no]) + return d->class_names [class_no]; + else { + sprintf (class_name, "class_%d", class_no); + return class_name; + } +} + +/* -- Code to dump individual script block types ------------------------- */ + +static void +script_dump_object(disasm_state_t *d, script_state_t *s, + unsigned char *data, int seeker, int objsize, int pass_no) +{ + int selectors, overloads, selectorsize; + int species = getInt16(data + 8 + seeker); + int superclass = getInt16(data + 10 + seeker); + int namepos = getInt16(data + 14 + seeker); + int i = 0; + short sel; + const char *name; + char buf [256]; + short *sels; + + selectors = (selectorsize = getInt16(data + seeker + 6)); + name=namepos? ((const char *)data + namepos) : ""; + + if (pass_no == 1) + script_add_area (s, seeker, seeker+objsize-1, area_object, strdup(name)); + + if (pass_no == 2) { + sciprintf(".object\n"); + sciprintf("Name: %s\n", name); + sciprintf("Superclass: %s [%x]\n", get_class_name (d, superclass), superclass); + sciprintf("Species: %s [%x]\n", get_class_name (d, species), species); + + sciprintf("-info-:%x\n", getInt16(data + 12 + seeker) & 0xffff); + + sciprintf("Function area offset: %x\n", getInt16(data + seeker + 4)); + sciprintf("Selectors [%x]:\n", selectors); + } + + seeker += 8; + + if (species < d->class_count) + sels=d->class_selectors [species]; + else + sels=NULL; + + while (selectors--) { + if (pass_no == 2) { + sel=getInt16(data + seeker) & 0xffff; + if (sels && (sels [i] >= 0) && (sels[i] < d->selector_count)) { + sciprintf(" [#%03x] %s = 0x%x\n", i, d->snames [sels [i]], sel); + i++; + } + else + sciprintf(" [#%03x] = 0x%x\n", i++, sel); + } + + seeker += 2; + } + + selectors = overloads = getInt16(data + seeker); + + if (pass_no == 2) + sciprintf("Overloaded functions: %x\n", overloads); + + seeker += 2; + + while (overloads--) { + word selector = getInt16(data + (seeker)) & 0xffff; + if (d->old_header) selector >>= 1; + + if (pass_no == 1) { + sprintf (buf, "%s::%s", name, get_selector_name (d, selector)); + script_add_name (s, getInt16(data + seeker + selectors*2 + 2), buf, species); + } + else { + sciprintf(" [%03x] %s: @", selector, get_selector_name (d, selector)); + sciprintf("%04x\n", getInt16(data + seeker + selectors*2 + 2)); + } + + seeker += 2; + } +} + +static void +script_dump_class(disasm_state_t *d, script_state_t *s, + unsigned char *data, int seeker, int objsize, int pass_no) +{ + word selectors, overloads, selectorsize; + int species = getInt16(data + 8 + seeker); + int superclass = getInt16(data + 10 + seeker); + int namepos = getInt16(data + 14 + seeker); + const char *name; + char buf [256]; + int i; + + name=namepos? ((const char *)data + namepos) : ""; + selectors = (selectorsize = getInt16(data + seeker + 6)); + + if (pass_no == 1) { + if (species >= 0 && species < d->class_count) { + if (!namepos) { + sprintf (buf, "class_%d", species); + d->class_names [species] = sci_strdup (buf); + } + else + d->class_names [species] = sci_strdup (name); + + d->class_selector_count [species] = selectors; + d->class_selectors [species] = (short *) sci_malloc (sizeof (short) * selectors); + } + } + + if (pass_no == 2) { + sciprintf (".class\n"); + sciprintf("Name: %s\n", name); + sciprintf("Superclass: %s [%x]\n", get_class_name (d, superclass), superclass); + sciprintf("Species: %x\n", species); + sciprintf("-info-:%x\n", getInt16(data + 12 + seeker) & 0xffff); + + sciprintf("Function area offset: %x\n", getInt16(data + seeker + 4)); + sciprintf("Selectors [%x]:\n", selectors); + } + + seeker += 8; + selectorsize <<= 1; + + for (i=0; iold_header) selector >>= 1; + + if (pass_no == 1) { + if (species >= 0 && species < d->class_count) + d->class_selectors [species][i] = selector; + } + else + sciprintf(" [%03x] %s = 0x%x\n", selector, get_selector_name (d, selector), + getInt16(data + seeker) & 0xffff); + + seeker += 2; + } + + seeker += selectorsize; + + selectors = overloads = getInt16(data + seeker); + + sciprintf("Overloaded functions: %x\n", overloads); + + seeker += 2; + + while (overloads--) { + word selector = getInt16(data + (seeker)) & 0xffff; + if (d->old_header) selector >>= 1; + + if (pass_no == 1) { + sprintf (buf, "%s::%s", name, get_selector_name (d, selector)); + script_add_name (s, getInt16(data + seeker + selectors*2 + 2) & 0xffff, buf, species); + } + else { + sciprintf(" [%03x] %s: @", selector & 0xffff, get_selector_name (d, selector)); + sciprintf("%04x\n", getInt16(data + seeker + selectors*2 + 2) & 0xffff); + } + + seeker += 2; + } +} + +static int +script_dump_said_string(disasm_state_t *d, unsigned char *data, int seeker) +{ + while (1) { + unsigned short nextitem=(unsigned char) data [seeker++]; + if (nextitem == 0xFF) return seeker; + + if (nextitem >= 0xF0) { + switch (nextitem) { + case 0xf0: sciprintf(", "); break; + case 0xf1: sciprintf("& "); break; + case 0xf2: sciprintf("/ "); break; + case 0xf3: sciprintf("( "); break; + case 0xf4: sciprintf(") "); break; + case 0xf5: sciprintf("[ "); break; + case 0xf6: sciprintf("] "); break; + case 0xf7: sciprintf("# "); break; + case 0xf8: sciprintf("< "); break; + case 0xf9: sciprintf("> "); break; + } + } + else { + nextitem = nextitem << 8 | (unsigned char) data [seeker++]; + sciprintf ("%s ", vocab_get_any_group_word (nextitem, d->words, d->word_count)); + if (verbose) + sciprintf ("[%03x] ", nextitem); + } + } +} + +static void +script_dump_said(disasm_state_t *d, script_state_t *s, + unsigned char *data, int seeker, int objsize, int pass_no) +{ + int _seeker=seeker+objsize-4; + + if (pass_no == 1) { + script_add_area (s, seeker, seeker+objsize-1, area_said, NULL); + return; + } + + sciprintf (".said\n"); + + while (seeker < _seeker-1) { + sciprintf ("%04x: ", seeker); + seeker=script_dump_said_string (d, data, seeker); + sciprintf ("\n"); + } +} + +static void +script_dump_synonyms(disasm_state_t *d, script_state_t *s, + unsigned char *data, int seeker, int objsize, int pass_no) +{ + int _seeker=seeker+objsize-4; + + sciprintf ("Synonyms:\n"); + while (seeker < _seeker) { + int search=getInt16(data+seeker); + int replace=getInt16(data+seeker+2); + seeker+=4; + if (search<0) break; + sciprintf ("%s[%03x] ==> %s[%03x]\n", + vocab_get_any_group_word (search, d->words, d->word_count), search, + vocab_get_any_group_word (replace, d->words, d->word_count), replace); + } +} + +static void +script_dump_strings(disasm_state_t *d, script_state_t *s, + unsigned char *data, int seeker, int objsize, int pass_no) +{ + int endptr=seeker+objsize-4; + + if (pass_no == 1) { + script_add_area (s, seeker, seeker+objsize-1, area_string, NULL); + return; + } + + sciprintf(".strings\n"); + while (data [seeker] && seeker < endptr) { + sciprintf ("%04x: %s\n", seeker, data+seeker); + seeker += strlen ((char *) data+seeker)+1; + } +} + +static void +script_dump_exports(disasm_state_t *d, script_state_t *s, + unsigned char *data, int seeker, int objsize, int pass_no) +{ + byte *pexport=(byte *) (data+seeker); + word export_count=getUInt16(pexport); + int i; + char buf [256]; + + pexport += 2; + + if (pass_no == 2) sciprintf (".exports\n"); + + for (i=0; i> 1; + word param_value; + char *name; + + opsize &= 1; /* byte if true, word if false */ + + if (pass_no == 2) { + name=script_find_name (s, seeker, &cur_class); + if (name) sciprintf (" %s:\n", name); + sciprintf("%04X: ", seeker); + sciprintf("%s", d->opcodes[opcode].name); + if (opcode_size && formats[opcode][0]) + sciprintf (".%c", opsize? 'b' : 'w'); + sciprintf ("\t"); + } + + seeker++; + + for (i=0; formats[opcode][i]; i++) + + switch (formats[opcode][i]) { + + case Script_Invalid: + if (pass_no == 2) sciprintf("-Invalid operation-"); + break; + + case Script_SByte: + case Script_Byte: + if (pass_no == 2) sciprintf(" %02x", data[seeker]); + seeker++; + break; + + case Script_Word: + case Script_SWord: + if (pass_no == 2) + sciprintf(" %04x", 0xffff & (data[seeker] | (data[seeker+1] << 8))); + seeker += 2; + break; + + case Script_SVariable: + case Script_Variable: + case Script_Global: + case Script_Local: + case Script_Temp: + case Script_Param: + case Script_SRelative: + case Script_Property: + case Script_Offset: + if (opsize) + param_value = data [seeker++]; + else { + param_value = 0xffff & (data[seeker] | (data[seeker+1] << 8)); + seeker += 2; + } + + if (pass_no == 1) { + if (opcode == op_jmp || opcode == op_bt || opcode == op_bnt) { + dest=seeker+(short) param_value; + sprintf (buf, "lbl_%04X", dest); + script_add_name (s, dest, buf, -2); + } + } + else if (pass_no == 2) + switch (formats[opcode][i]) { + + case Script_SVariable: + case Script_Variable: + if (opcode == op_callk) { + sciprintf(" #%s", (param_value < d->kernel_names_nr) + ? d->kernel_names[param_value] : ""); + if (verbose) sciprintf ("[%x]", param_value); + } + else if (opcode == op_class || (opcode == op_super && i==0)) { + sciprintf (" %s", (d->class_names && param_value < d->class_count) + ? d->class_names[param_value] : ""); + if (verbose) sciprintf ("[%x]", param_value); + } + else sciprintf(opsize? " %02x" : " %04x", param_value); + + if (opcode == op_pushi && param_value > 0 && param_value < d->selector_count) + sciprintf ("\t\t; selector <%s>", d->snames [param_value]); + + break; + + case Script_Global: + sciprintf (" global_%d", param_value); + break; + + case Script_Local: + sciprintf (" local_%d", param_value); + break; + + case Script_Temp: + sciprintf (" temp_%d", param_value); + break; + + case Script_Param: + sciprintf (" param_%d", param_value); + break; + + case Script_Offset: + dest=(short) param_value; + dest_name=script_find_name (s, dest, NULL); + if (dest_name) + sciprintf (" %s", dest_name); + else + sciprintf (" %04x", dest); + + if (verbose) + sciprintf (opsize? " [%02x] " : " [%04x] ", param_value); + + if (opcode == op_lofsa || opcode == op_lofss){ + int atype=script_get_area_type (s, dest, &area_data); + if (atype == area_string) { + strncpy (buf, (char *) &data [dest], sizeof (buf)-1); + buf [sizeof (buf)-1] = 0; + if (strlen (buf) > 40){ + buf [40] = 0; + strcat (buf, "..."); + } + sciprintf ("\t\t; \"%s\"", buf); + } + else if (atype == area_said) { + sciprintf ("\t\t; said \""); + script_dump_said_string (d, data, dest); + sciprintf ("\"\n"); + } + else if (atype == area_object) + sciprintf ("\t\t; object <%s>", area_data); + } + break; + + case Script_SRelative: + dest=seeker+(short) param_value; + dest_name=script_find_name (s, dest, NULL); + if (dest_name) + sciprintf (" %s", dest_name); + else + sciprintf (" %04x", dest); + + if (verbose) + sciprintf (opsize? " [%02x] " : " [%04x] ", param_value); + + if (opcode == op_lofsa || opcode == op_lofss){ + int atype=script_get_area_type (s, dest, &area_data); + if (atype == area_string) { + strncpy (buf, (char *) &data [dest], sizeof (buf)-1); + buf [sizeof (buf)-1] = 0; + if (strlen (buf) > 40){ + buf [40] = 0; + strcat (buf, "..."); + } + sciprintf ("\t\t; \"%s\"", buf); + } + else if (atype == area_said) { + sciprintf ("\t\t; said \""); + script_dump_said_string (d, data, dest); + sciprintf ("\"\n"); + } + else if (atype == area_object) + sciprintf ("\t\t; object <%s>", area_data); + } + break; + + case Script_Property: + if (cur_class != -1 && param_value/2 < d->class_selector_count [cur_class]) { + sciprintf (" %s", get_selector_name (d, d->class_selectors [cur_class][param_value/2])); + if (verbose) sciprintf ("[%x]", param_value); + } + else + sciprintf(opsize? " %02x" : " %04x", param_value); + + break; + + case Script_End: + if (pass_no == 2) sciprintf ("\n"); + break; + + default: + sciprintf("Unexpected opcode format %d\n", (formats[opcode][i])); + } + + default: + break; + } + if (pass_no == 2) sciprintf ("\n"); + + } + +} + +void +disassemble_script_pass (disasm_state_t *d, script_state_t *s, + resource_t *script, int pass_no) +{ + int _seeker = 0; + word id=getInt16 (script->data); + + if (id > 15) { + if (pass_no == 2) sciprintf ("; Old script header detected\n"); + d->old_header = 1; + } + + if (d->old_header) _seeker = 2; + + while (_seeker < script->size) { + int objtype = getInt16(script->data + _seeker); + int objsize; + int seeker = _seeker + 4; + + if (!objtype) return; + + if (pass_no == 2) + sciprintf("\n"); + + objsize = getInt16(script->data + _seeker + 2); + + if (pass_no == 2) { + sciprintf("; Obj type #%x, offset 0x%x, size 0x%x:\n", objtype, _seeker, objsize); + if (hexdump) sci_hexdump(script->data + seeker, objsize -4, seeker); + } + + _seeker += objsize; + + switch (objtype) { + case sci_obj_object: + script_dump_object (d, s, script->data, seeker, objsize, pass_no); + break; + + case sci_obj_code: + script_disassemble_code (d, s, script->data, seeker, objsize, pass_no); + break; + + case sci_obj_synonyms: + script_dump_synonyms (d, s, script->data, seeker, objsize, pass_no); + break; + + case sci_obj_said: + script_dump_said (d, s, script->data, seeker, objsize, pass_no); + break; + + case sci_obj_strings: + script_dump_strings (d, s, script->data, seeker, objsize, pass_no); + break; + + case sci_obj_class: + script_dump_class (d, s, script->data, seeker, objsize, pass_no); + break; + + case sci_obj_exports: + script_dump_exports (d, s, script->data, seeker, objsize, pass_no); + break; + + case sci_obj_pointers: if (pass_no == 2) { + sciprintf("Pointers\n"); + sci_hexdump(script->data + seeker, objsize -4, seeker); + }; + break; + + case sci_obj_preload_text: if (pass_no == 2) { + sciprintf("The script has a preloaded text resource\n"); + }; + break; + + case sci_obj_localvars: if (pass_no == 2) { + sciprintf("Local vars\n"); + sci_hexdump(script->data + seeker, objsize -4, seeker); + }; + break; + + default: + sciprintf("Unsupported %d!\n", objtype); + return; + } + } + + sciprintf("Script ends without terminator\n"); +} + +void +disassemble_script(disasm_state_t *d, int res_no, int pass_no) +{ + resource_t *script = scir_find_resource(resmgr, sci_script, res_no, 0); + script_state_t *s = find_script_state(d, res_no); + + if (!script) { + sciprintf("Script not found!\n"); + return; + } + + disassemble_script_pass (d, s, script, pass_no); +} diff --git a/engines/sci/tools/scipack.c b/engines/sci/tools/scipack.c new file mode 100644 index 0000000000..a5b7a8d8a1 --- /dev/null +++ b/engines/sci/tools/scipack.c @@ -0,0 +1,217 @@ +/*************************************************************************** + scipack.c Copyright (C) 2002 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +# include +#endif + +#define COPY_BLOCK_SIZE 512 + +unsigned short *resource_ids = NULL; + +void +help() +{ + printf("Usage:\n\tscipack ... \n" + "\nBuilds an uncompressed SCI0 resource.000 and a resource.map\n"); +} + +int /* Returns resource ID on success, -1 on error */ +test_file(char *filename) +{ + char *dot = strchr(filename, '.'); + char *endptr; + FILE *f; + int res_type, res_index; + + if (!dot) { + fprintf(stderr, "Must contain a period"); + return -1; + } + + *dot = 0; + + for (res_type = 0; res_type < sci_invalid_resource + && strcasecmp(filename, sci_resource_types[res_type]); res_type++); + + *dot = '.'; + + if (res_type == sci_invalid_resource) { + fprintf(stderr, "Invalid resource type"); + return -1; + } + + ++dot; + res_index = strtol(dot, &endptr, 10); + + if (!*dot || *endptr) { + fprintf(stderr, "Invalid resource index"); + return -1; + } + + if (res_index < 0) { + fprintf(stderr, "Negative resource index"); + return -1; + } + + if (res_index >= 1000) { + fprintf(stderr, "Resource index too large"); + return -1; + } + + f = fopen(filename, "r"); + if (!f) { + perror("While asserting file"); + return -1; + } + fclose(f); + + return (res_type << 11) | res_index; +} + +int +build_file_ids(int count, char **names) +{ + int i; + int error = 0; + + resource_ids = (unsigned short*) malloc(sizeof(unsigned short) * count); + + for (i = 0; i < count; i++) { + int id = test_file(names[i]); + if (id < 0) { + error = -1; + fprintf(stderr, ": %s\n", names[i]); + } + else resource_ids[i] = id; + } + + return error; +} + + +static inline void +write_uint16(int fd, unsigned int uint) +{ + unsigned char upper = (uint >> 8) & 0xff; + unsigned char lower = (uint) & 0xff; + + if ((write(fd, &upper, 1) < 1) + || (write(fd, &lower, 1) < 1)) { + perror("While writing"); + exit(1); + } +} + +int +write_files(int count, char **names) +{ + int resource_000, resource_map; + int i; + + resource_000 = creat("resource.000", 0644); + if (resource_000 < 0) { + perror("While creating 'resource.000'"); + return -1; + } + + resource_map = creat("resource.map", 0644); + if (resource_map < 0) { + perror("While creating 'resource.map'"); + return -1; + } + + for (i = 0; i < count; i++) { + int fd = open(names[i], O_RDONLY); + struct stat fdstat; + int fdsize; + unsigned char buf[512]; + int j; + long offset = lseek(resource_000, SEEK_CUR, 0); + int top_offset = (offset >> 16) & 0xffff; + int bot_offset = offset & 0xffff; + + if (fd < 0) { + perror(names[i]); + return -1; + } + fstat(fd, &fdstat); + fdsize = fdstat.st_size; + + write_uint16(resource_000, resource_ids[i]); + write_uint16(resource_000, fdsize); + write_uint16(resource_000, fdsize); + write_uint16(resource_000, 0); + + do { + j = read(fd, buf, COPY_BLOCK_SIZE); + write(resource_000, buf, j); + } while (j == COPY_BLOCK_SIZE); + close(fd); + + write_uint16(resource_map, resource_ids[i]); + write_uint16(resource_map, bot_offset); + write_uint16(resource_map, top_offset); + } + + /* Terminate resource 000 */ + write_uint16(resource_000, 0); + + /* Terminate resource map */ + write_uint16(resource_map, 0xffff); + write_uint16(resource_map, 0xffff); + + close(resource_000); + close(resource_map); +} + + +int +main(int argc, char **argv) +{ + printf("scipack.c Copyright (C) 2002 Christoph Reichenbach\n" + "This program is FREE SOFTWARE. You may copy it and/or re-distribute it\n" + "according to the terms of the GNU General Public License. See LICENSING\n" + "for details.\n"); + + if (argc < 1) + help(); + + if (build_file_ids(argc - 1, argv + 1)) + return -1; + + if (write_files(argc - 1, argv + 1)) + return -1; + free(resource_ids); +} diff --git a/engines/sci/tools/sciunpack.c b/engines/sci/tools/sciunpack.c new file mode 100644 index 0000000000..e5b3b39a2e --- /dev/null +++ b/engines/sci/tools/sciunpack.c @@ -0,0 +1,482 @@ +/*************************************************************************** + sciunpack.c Copyright (C) 1999, 2000, 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + + History: + + 990327 - created (CJR) + +***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include "sciunpack.h" + +#include +#include +#include + +/* #define DRAW_GRAPHICS */ + +#undef HAVE_OBSTACK_H + +#ifdef _MSC_VER +# include +# define extern __declspec(dllimport) extern +#endif + +#ifdef HAVE_GETOPT_H +# ifndef _WIN32 +# include +# else +# include +# 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(_DOS) || 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] \n" + " sciunpack [options] [-U] \n" + "Unpacks resource data\n" + "If * is specified instead of , \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 .midi\n" + " script resources: Will be dissected and stored in .script\n" +#ifdef DRAW_GRAPHICS + " picture resources: Will be converted to PNG, stored in .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; iresources_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; iresources_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/engines/sci/tools/sciunpack.h b/engines/sci/tools/sciunpack.h new file mode 100644 index 0000000000..5553046d56 --- /dev/null +++ b/engines/sci/tools/sciunpack.h @@ -0,0 +1,70 @@ +/*************************************************************************** + sciunpack.h Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#ifndef SCI_UNPACK_H_ +#define SCI_UNPACK_H_ + +#include + +#define SORT_METHOD_ALPHA 0 +#define SORT_METHOD_GROUP 1 + +#define DEFAULT_SORTING SORT_METHOD_ALPHA + +extern int vocab_sort; /* Sorting strategy for vocab */ +extern resource_mgr_t *resmgr; + +int +vocab_print(void); +/* Prints vocab data +** Parameters: (void) +** Returns : (int) 0 on success, 1 on failure +** Controlled by vocab_sort +*/ + +int +script_dump(void); +/* Prints all object information +** Parameters: (void) +** Returns : (int) 0 on success, 1 on failure +*/ + +int +vocab_dump(void); +/* Prints full vocabulary information +** Parameters: (void) +** Returns : (int) 0 on success, 1 on failure +*/ + +int +print_classes(void); +/* Prints full class information +** Parameters: (void) +** Returns : (int) 0 on success, 1 otherwise +*/ + +#endif diff --git a/engines/sci/tools/scriptdump.c b/engines/sci/tools/scriptdump.c new file mode 100644 index 0000000000..6efe150ba2 --- /dev/null +++ b/engines/sci/tools/scriptdump.c @@ -0,0 +1,49 @@ +/*************************************************************************** + scriptdump.c Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/engines/sci/tools/vocabdump.c b/engines/sci/tools/vocabdump.c new file mode 100644 index 0000000000..5dcf72a09b --- /dev/null +++ b/engines/sci/tools/vocabdump.c @@ -0,0 +1,77 @@ +/*************************************************************************** + vocabdump.c Copyright (C) 1999 Christoph Reichenbach, TU Darmstadt + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + + History: + + 990504 - created (CJR) + +***************************************************************************/ + +#include +#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. */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#include +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#endif /* GNU C library. */ + +/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a + long-named option. Because this is not POSIX.2 compliant, it is + being phased out. */ +/* #define GETOPT_COMPAT */ + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = 0; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +#define BAD_OPTION '\0' +int optopt = BAD_OPTION; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include +#define my_index strchr +#define my_strlen strlen +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#if __STDC__ || defined(PROTO) +extern char *getenv(const char *name); +extern int strcmp (const char *s1, const char *s2); +extern int strncmp(const char *s1, const char *s2, int n); + +static int my_strlen(const char *s); +static char *my_index (const char *str, int chr); +#else +extern char *getenv (); +#endif + +static int +my_strlen (str) + const char *str; +{ + int n = 0; + while (*str++) + n++; + return n; +} + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +#endif /* GNU C library. */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. + + To perform the swap, we first reverse the order of all elements. So + all options now come before all non options, but they are in the + wrong order. So we put back the options and non options in original + order by reversing them again. For example: + original input: a b c -x -y + reverse all: -y -x c b a + reverse options: -x -y c b a + reverse non options: -x -y a b c +*/ + +#if __STDC__ || defined(PROTO) +static void exchange (char **argv); +#endif + +static void +exchange (argv) + char **argv; +{ + char *temp, **first, **last; + + /* Reverse all the elements [first_nonopt, optind) */ + first = &argv[first_nonopt]; + last = &argv[optind-1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } + /* Put back the options in order */ + first = &argv[first_nonopt]; + first_nonopt += (optind - last_nonopt); + last = &argv[first_nonopt - 1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } + + /* Put back the non options in order */ + first = &argv[first_nonopt]; + last_nonopt = optind; + last = &argv[last_nonopt-1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return BAD_OPTION after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return BAD_OPTION. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + int option_index; + + optarg = 0; + + /* Initialize the internal data when the first call is made. + Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + if (optind == 0) + { + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (getenv ("POSIXLY_CORRECT") != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + } + + if (nextchar == NULL || *nextchar == '\0') + { + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Now skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + optind++; + last_nonopt = optind; + } + + /* Special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Start decoding its characters. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + if (longopts != NULL + && ((argv[optind][0] == '-' + && (argv[optind][1] == '-' || long_only)) +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + )) + { + const struct option *p; + char *s = nextchar; + int exact = 0; + int ambig = 0; + const struct option *pfound = NULL; + int indfound = 0; + + while (*s && *s != '=') + s++; + + /* Test all options for either exact match or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; + p++, option_index++) + if (!strncmp (p->name, nextchar, s - nextchar)) + { + if (s - nextchar == my_strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += my_strlen (nextchar); + optind++; + return BAD_OPTION; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*s) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = s + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += my_strlen (nextchar); + return BAD_OPTION; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += my_strlen (nextchar); + return optstring[0] == ':' ? ':' : BAD_OPTION; + } + } + nextchar += my_strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + return BAD_OPTION; + } + } + + /* Look at and handle the next option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { +#if 0 + if (c < 040 || c >= 0177) + fprintf (stderr, "%s: unrecognized option, character code 0%o\n", + argv[0], c); + else + fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); +#endif + } + optopt = c; + return BAD_OPTION; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = 0; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { +#if 0 + fprintf (stderr, "%s: option `-%c' requires an argument\n", + argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = BAD_OPTION; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case BAD_OPTION: + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/engines/sci/win32/msvc71-2003/freesci.sln b/engines/sci/win32/msvc71-2003/freesci.sln new file mode 100644 index 0000000000..0863329a64 --- /dev/null +++ b/engines/sci/win32/msvc71-2003/freesci.sln @@ -0,0 +1,34 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fsci_dll", "fsci_dll\fsci_dll.vcproj", "{B605B919-02BD-4E2D-B043-08A21736B01A}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sciv", "sciv\sciv.vcproj", "{6BF71632-BD06-4D48-AE7C-21C6EB76CACA}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {B605B919-02BD-4E2D-B043-08A21736B01A}.Debug.ActiveCfg = Debug|Win32 + {B605B919-02BD-4E2D-B043-08A21736B01A}.Debug.Build.0 = Debug|Win32 + {B605B919-02BD-4E2D-B043-08A21736B01A}.Release.ActiveCfg = Release|Win32 + {B605B919-02BD-4E2D-B043-08A21736B01A}.Release.Build.0 = Release|Win32 + {6BF71632-BD06-4D48-AE7C-21C6EB76CACA}.Debug.ActiveCfg = Debug|Win32 + {6BF71632-BD06-4D48-AE7C-21C6EB76CACA}.Debug.Build.0 = Debug|Win32 + {6BF71632-BD06-4D48-AE7C-21C6EB76CACA}.Release.ActiveCfg = Release|Win32 + {6BF71632-BD06-4D48-AE7C-21C6EB76CACA}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection + GlobalSection(DPCodeReviewSolutionGUID) = preSolution + DPCodeReviewSolutionGUID = {00000000-0000-0000-0000-000000000000} + EndGlobalSection +EndGlobal diff --git a/engines/sci/win32/msvc71-2003/fsci_dll/fsci_dll.vcproj b/engines/sci/win32/msvc71-2003/fsci_dll/fsci_dll.vcproj new file mode 100644 index 0000000000..f37dfd7258 --- /dev/null +++ b/engines/sci/win32/msvc71-2003/fsci_dll/fsci_dll.vcproj @@ -0,0 +1,932 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engines/sci/win32/msvc71-2003/sciv/sciv.vcproj b/engines/sci/win32/msvc71-2003/sciv/sciv.vcproj new file mode 100644 index 0000000000..4f837bb0ef --- /dev/null +++ b/engines/sci/win32/msvc71-2003/sciv/sciv.vcproj @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engines/sci/win32/msvc80-2005/freesci.sln b/engines/sci/win32/msvc80-2005/freesci.sln new file mode 100644 index 0000000000..6388360066 --- /dev/null +++ b/engines/sci/win32/msvc80-2005/freesci.sln @@ -0,0 +1,28 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fsci_dll", "fsci_dll\fsci_dll.vcproj", "{B605B919-02BD-4E2D-B043-08A21736B01A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sciv", "sciv\sciv.vcproj", "{6BF71632-BD06-4D48-AE7C-21C6EB76CACA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B605B919-02BD-4E2D-B043-08A21736B01A}.Debug|Win32.ActiveCfg = Debug|Win32 + {B605B919-02BD-4E2D-B043-08A21736B01A}.Debug|Win32.Build.0 = Debug|Win32 + {B605B919-02BD-4E2D-B043-08A21736B01A}.Release|Win32.ActiveCfg = Release|Win32 + {B605B919-02BD-4E2D-B043-08A21736B01A}.Release|Win32.Build.0 = Release|Win32 + {6BF71632-BD06-4D48-AE7C-21C6EB76CACA}.Debug|Win32.ActiveCfg = Debug|Win32 + {6BF71632-BD06-4D48-AE7C-21C6EB76CACA}.Debug|Win32.Build.0 = Debug|Win32 + {6BF71632-BD06-4D48-AE7C-21C6EB76CACA}.Release|Win32.ActiveCfg = Release|Win32 + {6BF71632-BD06-4D48-AE7C-21C6EB76CACA}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(DPCodeReviewSolutionGUID) = preSolution + DPCodeReviewSolutionGUID = {00000000-0000-0000-0000-000000000000} + EndGlobalSection +EndGlobal diff --git a/engines/sci/win32/msvc80-2005/fsci_dll/fsci_dll.vcproj b/engines/sci/win32/msvc80-2005/fsci_dll/fsci_dll.vcproj new file mode 100644 index 0000000000..1e37611f2f --- /dev/null +++ b/engines/sci/win32/msvc80-2005/fsci_dll/fsci_dll.vcproj @@ -0,0 +1,1218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engines/sci/win32/msvc80-2005/sciv/sciv.vcproj b/engines/sci/win32/msvc80-2005/sciv/sciv.vcproj new file mode 100644 index 0000000000..1c0a302c4e --- /dev/null +++ b/engines/sci/win32/msvc80-2005/sciv/sciv.vcproj @@ -0,0 +1,260 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engines/sci/win32/msvc90-2008/freesci.sln b/engines/sci/win32/msvc90-2008/freesci.sln new file mode 100644 index 0000000000..7a3e60c904 --- /dev/null +++ b/engines/sci/win32/msvc90-2008/freesci.sln @@ -0,0 +1,28 @@ +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fsci_dll", "fsci_dll\fsci_dll.vcproj", "{B605B919-02BD-4E2D-B043-08A21736B01A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sciv", "sciv\sciv.vcproj", "{6BF71632-BD06-4D48-AE7C-21C6EB76CACA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B605B919-02BD-4E2D-B043-08A21736B01A}.Debug|Win32.ActiveCfg = Debug|Win32 + {B605B919-02BD-4E2D-B043-08A21736B01A}.Debug|Win32.Build.0 = Debug|Win32 + {B605B919-02BD-4E2D-B043-08A21736B01A}.Release|Win32.ActiveCfg = Release|Win32 + {B605B919-02BD-4E2D-B043-08A21736B01A}.Release|Win32.Build.0 = Release|Win32 + {6BF71632-BD06-4D48-AE7C-21C6EB76CACA}.Debug|Win32.ActiveCfg = Debug|Win32 + {6BF71632-BD06-4D48-AE7C-21C6EB76CACA}.Debug|Win32.Build.0 = Debug|Win32 + {6BF71632-BD06-4D48-AE7C-21C6EB76CACA}.Release|Win32.ActiveCfg = Release|Win32 + {6BF71632-BD06-4D48-AE7C-21C6EB76CACA}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(DPCodeReviewSolutionGUID) = preSolution + DPCodeReviewSolutionGUID = {00000000-0000-0000-0000-000000000000} + EndGlobalSection +EndGlobal diff --git a/engines/sci/win32/msvc90-2008/fsci_dll/fsci_dll.vcproj b/engines/sci/win32/msvc90-2008/fsci_dll/fsci_dll.vcproj new file mode 100644 index 0000000000..f7e98c3a39 --- /dev/null +++ b/engines/sci/win32/msvc90-2008/fsci_dll/fsci_dll.vcproj @@ -0,0 +1,1223 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engines/sci/win32/msvc90-2008/sciv/sciv.vcproj b/engines/sci/win32/msvc90-2008/sciv/sciv.vcproj new file mode 100644 index 0000000000..c00d525f75 --- /dev/null +++ b/engines/sci/win32/msvc90-2008/sciv/sciv.vcproj @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engines/sci/win32/usleep.c b/engines/sci/win32/usleep.c new file mode 100644 index 0000000000..b175a2b4e2 --- /dev/null +++ b/engines/sci/win32/usleep.c @@ -0,0 +1,25 @@ +#ifdef _WIN32 + +#include + +extern void +usleep (long usec) +{ + LARGE_INTEGER lFrequency; + LARGE_INTEGER lEndTime; + LARGE_INTEGER lCurTime; + + QueryPerformanceFrequency (&lFrequency); + if (lFrequency.QuadPart) + { + QueryPerformanceCounter (&lEndTime); + lEndTime.QuadPart += (LONGLONG) usec * lFrequency.QuadPart / 1000000; + do + { + QueryPerformanceCounter (&lCurTime); + Sleep(0); + } while (lCurTime.QuadPart < lEndTime.QuadPart); + } +} + +#endif diff --git a/engines/sci/yywrap.c b/engines/sci/yywrap.c new file mode 100644 index 0000000000..9bc119b735 --- /dev/null +++ b/engines/sci/yywrap.c @@ -0,0 +1,4 @@ +int yywrap() +{ + return 1; +} -- cgit v1.2.3