From e241843bec22600ab4ef98e7a085e82aac73fc93 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Sun, 15 Feb 2009 11:39:07 +0000 Subject: - Remove some unneeded files - Mass rename .c to .cpp svn-id: r38227 --- engines/sci/engine/game.c | 773 --- engines/sci/engine/game.cpp | 773 +++ engines/sci/engine/gc.c | 334 -- engines/sci/engine/gc.cpp | 334 ++ engines/sci/engine/grammar.c | 720 --- engines/sci/engine/grammar.cpp | 720 +++ engines/sci/engine/heap.c | 298 -- engines/sci/engine/heap.cpp | 298 ++ engines/sci/engine/kernel.c | 1093 ---- engines/sci/engine/kernel.cpp | 1093 ++++ engines/sci/engine/kevent.c | 229 - engines/sci/engine/kevent.cpp | 229 + engines/sci/engine/kfile.c | 1175 ----- engines/sci/engine/kfile.cpp | 1175 +++++ engines/sci/engine/kgraphics.c | 3627 ------------- engines/sci/engine/kgraphics.cpp | 3627 +++++++++++++ engines/sci/engine/klists.c | 608 --- engines/sci/engine/klists.cpp | 608 +++ engines/sci/engine/kmath.c | 201 - engines/sci/engine/kmath.cpp | 201 + engines/sci/engine/kmenu.c | 533 -- engines/sci/engine/kmenu.cpp | 533 ++ engines/sci/engine/kmovement.c | 580 -- engines/sci/engine/kmovement.cpp | 580 ++ engines/sci/engine/kpathing.c | 1734 ------ engines/sci/engine/kpathing.cpp | 1734 ++++++ engines/sci/engine/kscripts.c | 353 -- engines/sci/engine/kscripts.cpp | 353 ++ engines/sci/engine/ksound.c | 948 ---- engines/sci/engine/ksound.cpp | 948 ++++ engines/sci/engine/kstring.c | 802 --- engines/sci/engine/kstring.cpp | 802 +++ engines/sci/engine/makefile.dos | 22 - engines/sci/engine/message.c | 226 - engines/sci/engine/message.cpp | 226 + engines/sci/engine/said.c | 2561 --------- engines/sci/engine/said.cpp | 2561 +++++++++ engines/sci/engine/savegame.c | 5395 ------------------- engines/sci/engine/savegame.cpp | 5395 +++++++++++++++++++ engines/sci/engine/scriptconsole.c | 1342 ----- engines/sci/engine/scriptconsole.cpp | 1342 +++++ engines/sci/engine/scriptdebug.c | 3904 -------------- engines/sci/engine/scriptdebug.cpp | 3904 ++++++++++++++ engines/sci/engine/seg_manager.c | 2063 -------- engines/sci/engine/seg_manager.cpp | 2063 ++++++++ engines/sci/engine/sys_strings.c | 119 - engines/sci/engine/sys_strings.cpp | 119 + engines/sci/engine/vm.c | 2410 --------- engines/sci/engine/vm.cpp | 2410 +++++++++ engines/sci/gfx/alpha_mvi_crossblit.c | 360 -- engines/sci/gfx/alpha_mvi_crossblit.cpp | 360 ++ engines/sci/gfx/antialias.c | 163 - engines/sci/gfx/antialias.cpp | 163 + engines/sci/gfx/font-5x8.c | 2580 --------- engines/sci/gfx/font-5x8.cpp | 2580 +++++++++ engines/sci/gfx/font-6x10.c | 3092 ----------- engines/sci/gfx/font-6x10.cpp | 3092 +++++++++++ engines/sci/gfx/font.c | 385 -- engines/sci/gfx/font.cpp | 385 ++ engines/sci/gfx/gfx_crossblit.c | 107 - engines/sci/gfx/gfx_crossblit.cpp | 107 + engines/sci/gfx/gfx_line.c | 96 - engines/sci/gfx/gfx_line.cpp | 96 + engines/sci/gfx/gfx_pixmap_scale.c | 497 -- engines/sci/gfx/gfx_pixmap_scale.cpp | 497 ++ engines/sci/gfx/gfx_res_options.c | 652 --- engines/sci/gfx/gfx_res_options.cpp | 652 +++ engines/sci/gfx/gfx_resource.c | 434 -- engines/sci/gfx/gfx_resource.cpp | 434 ++ engines/sci/gfx/gfx_support.c | 450 -- engines/sci/gfx/gfx_support.cpp | 450 ++ engines/sci/gfx/gfx_test.c | 1504 ------ engines/sci/gfx/gfx_test.cpp | 1504 ++++++ engines/sci/gfx/gfx_tools.c | 465 -- engines/sci/gfx/gfx_tools.cpp | 465 ++ engines/sci/gfx/menubar.c | 536 -- engines/sci/gfx/menubar.cpp | 536 ++ engines/sci/gfx/operations.c | 2490 --------- engines/sci/gfx/operations.cpp | 2490 +++++++++ engines/sci/gfx/resmgr.c | 708 --- engines/sci/gfx/resmgr.cpp | 708 +++ engines/sci/gfx/resource/sci_cursor_0.c | 107 - engines/sci/gfx/resource/sci_cursor_0.cpp | 107 + engines/sci/gfx/resource/sci_font.c | 153 - engines/sci/gfx/resource/sci_font.cpp | 153 + engines/sci/gfx/resource/sci_pal_1.c | 178 - engines/sci/gfx/resource/sci_pal_1.cpp | 178 + engines/sci/gfx/resource/sci_pic_0.c | 2023 ------- engines/sci/gfx/resource/sci_pic_0.cpp | 2023 +++++++ engines/sci/gfx/resource/sci_picfill.c | 429 -- engines/sci/gfx/resource/sci_picfill.cpp | 429 ++ engines/sci/gfx/resource/sci_picfill_aux.c | 205 - engines/sci/gfx/resource/sci_picfill_aux.cpp | 205 + engines/sci/gfx/resource/sci_resmgr.c | 341 -- engines/sci/gfx/resource/sci_resmgr.cpp | 341 ++ engines/sci/gfx/resource/sci_view_0.c | 254 - engines/sci/gfx/resource/sci_view_0.cpp | 254 + engines/sci/gfx/resource/sci_view_1.c | 554 -- engines/sci/gfx/resource/sci_view_1.cpp | 554 ++ engines/sci/gfx/sbtree.c | 473 -- engines/sci/gfx/sbtree.cpp | 473 ++ engines/sci/gfx/sci_widgets.c | 765 --- engines/sci/gfx/sci_widgets.cpp | 765 +++ engines/sci/gfx/widgets.c | 2600 --------- engines/sci/gfx/widgets.cpp | 2600 +++++++++ engines/sci/gfx/wrapper.c | 26 - engines/sci/menu/game_select_init.c | 275 - engines/sci/menu/game_select_screen.c | 581 --- engines/sci/module.mk | 5 - engines/sci/scicore/aatree.c | 181 - engines/sci/scicore/aatree.cpp | 181 + engines/sci/scicore/console.c | 151 - engines/sci/scicore/console.cpp | 151 + engines/sci/scicore/decompress0.c | 372 -- engines/sci/scicore/decompress0.cpp | 372 ++ engines/sci/scicore/decompress01.c | 655 --- engines/sci/scicore/decompress01.cpp | 655 +++ engines/sci/scicore/decompress1.c | 443 -- engines/sci/scicore/decompress1.cpp | 443 ++ engines/sci/scicore/decompress11.c | 167 - engines/sci/scicore/decompress11.cpp | 167 + engines/sci/scicore/exe.c | 86 - engines/sci/scicore/exe.cpp | 86 + engines/sci/scicore/exe_lzexe.c | 342 -- engines/sci/scicore/exe_lzexe.cpp | 342 ++ engines/sci/scicore/exe_raw.c | 73 - engines/sci/scicore/exe_raw.cpp | 73 + engines/sci/scicore/fnmatch.c | 847 --- engines/sci/scicore/fnmatch.cpp | 847 +++ engines/sci/scicore/hashmap.c | 186 - engines/sci/scicore/hashmap.cpp | 186 + engines/sci/scicore/int_hashmap.c | 33 - engines/sci/scicore/int_hashmap.cpp | 33 + engines/sci/scicore/modules.c | 153 - engines/sci/scicore/modules.cpp | 153 + engines/sci/scicore/old_objects.c | 716 --- engines/sci/scicore/old_objects.cpp | 716 +++ engines/sci/scicore/reg_t_hashmap.c | 42 - engines/sci/scicore/reg_t_hashmap.cpp | 42 + engines/sci/scicore/resource.c | 951 ---- engines/sci/scicore/resource.cpp | 951 ++++ engines/sci/scicore/resource_map.c | 515 -- engines/sci/scicore/resource_map.cpp | 515 ++ engines/sci/scicore/resource_patch.c | 227 - engines/sci/scicore/resource_patch.cpp | 227 + engines/sci/scicore/resourcecheck.c | 38 - engines/sci/scicore/resourcecheck.cpp | 38 + engines/sci/scicore/sci_memory.c | 311 -- engines/sci/scicore/sci_memory.cpp | 311 ++ engines/sci/scicore/script.c | 488 -- engines/sci/scicore/script.cpp | 488 ++ engines/sci/scicore/tools.c | 798 --- engines/sci/scicore/tools.cpp | 798 +++ engines/sci/scicore/versions.c | 368 -- engines/sci/scicore/versions.cpp | 368 ++ engines/sci/scicore/vocab.c | 713 --- engines/sci/scicore/vocab.cpp | 713 +++ engines/sci/scicore/vocab_debug.c | 421 -- engines/sci/scicore/vocab_debug.cpp | 421 ++ engines/sci/sfx/adlib.c | 66 - engines/sci/sfx/adlib.cpp | 66 + engines/sci/sfx/core.c | 938 ---- engines/sci/sfx/core.cpp | 938 ++++ engines/sci/sfx/device/alsa-midi.c | 227 - engines/sci/sfx/device/alsa-midi.cpp | 227 + engines/sci/sfx/device/camd-midi.c | 161 - engines/sci/sfx/device/camd-midi.cpp | 161 + engines/sci/sfx/device/devices.c | 112 - engines/sci/sfx/device/devices.cpp | 112 + engines/sci/sfx/device/unixraw-midi.c | 100 - engines/sci/sfx/device/unixraw-midi.cpp | 100 + engines/sci/sfx/iterator.c | 2113 -------- engines/sci/sfx/iterator.cpp | 2113 ++++++++ engines/sci/sfx/lists/gm_patches.c | 198 - engines/sci/sfx/lists/gm_patches.cpp | 198 + engines/sci/sfx/lists/mt32_timbres.c | 181 - engines/sci/sfx/lists/mt32_timbres.cpp | 181 + engines/sci/sfx/mixer/dc.c | 329 -- engines/sci/sfx/mixer/dc.cpp | 329 ++ engines/sci/sfx/mixer/mixers.c | 55 - engines/sci/sfx/mixer/mixers.cpp | 55 + engines/sci/sfx/mixer/soft.c | 988 ---- engines/sci/sfx/mixer/soft.cpp | 988 ++++ engines/sci/sfx/mixer/test.c | 351 -- engines/sci/sfx/mixer/test.cpp | 351 ++ engines/sci/sfx/pcm-iterator.c | 117 - engines/sci/sfx/pcm-iterator.cpp | 117 + engines/sci/sfx/pcm_device/alsa.c | 387 -- engines/sci/sfx/pcm_device/alsa.cpp | 387 ++ engines/sci/sfx/pcm_device/audbuf_test.c | 190 - engines/sci/sfx/pcm_device/audbuf_test.cpp | 190 + engines/sci/sfx/pcm_device/audiobuf.c | 348 -- engines/sci/sfx/pcm_device/audiobuf.cpp | 348 ++ engines/sci/sfx/pcm_device/pcm_devices.c | 73 - engines/sci/sfx/pcm_device/pcm_devices.cpp | 73 + engines/sci/sfx/pcm_device/sdl.c | 272 - engines/sci/sfx/pcm_device/sdl.cpp | 272 + engines/sci/sfx/player/players.c | 54 - engines/sci/sfx/player/players.cpp | 54 + engines/sci/sfx/player/polled.c | 335 -- engines/sci/sfx/player/polled.cpp | 335 ++ engines/sci/sfx/player/realtime.c | 328 -- engines/sci/sfx/player/realtime.cpp | 328 ++ engines/sci/sfx/seq/gm.c | 185 - engines/sci/sfx/seq/gm.cpp | 185 + engines/sci/sfx/seq/instrument-map.c | 539 -- engines/sci/sfx/seq/instrument-map.cpp | 539 ++ engines/sci/sfx/seq/map-mt32-to-gm.c | 813 --- engines/sci/sfx/seq/map-mt32-to-gm.cpp | 813 +++ engines/sci/sfx/seq/mt32.c | 480 -- engines/sci/sfx/seq/mt32.cpp | 480 ++ engines/sci/sfx/seq/oss-adlib.c | 374 -- engines/sci/sfx/seq/oss-adlib.cpp | 374 ++ engines/sci/sfx/seq/sequencers.c | 68 - engines/sci/sfx/seq/sequencers.cpp | 68 + engines/sci/sfx/softseq/SN76496.c | 248 - engines/sci/sfx/softseq/SN76496.cpp | 248 + engines/sci/sfx/softseq/amiga.c | 658 --- engines/sci/sfx/softseq/amiga.cpp | 658 +++ engines/sci/sfx/softseq/fluidsynth.c | 262 - engines/sci/sfx/softseq/fluidsynth.cpp | 262 + engines/sci/sfx/softseq/opl2.c | 718 --- engines/sci/sfx/softseq/opl2.cpp | 718 +++ engines/sci/sfx/softseq/pcspeaker.c | 184 - engines/sci/sfx/softseq/pcspeaker.cpp | 184 + engines/sci/sfx/softseq/softsequencers.c | 71 - engines/sci/sfx/softseq/softsequencers.cpp | 71 + engines/sci/sfx/songlib.c | 282 - engines/sci/sfx/songlib.cpp | 282 + engines/sci/sfx/test-iterator.c | 450 -- engines/sci/sfx/test-iterator.cpp | 450 ++ engines/sci/sfx/time.c | 131 - engines/sci/sfx/time.cpp | 131 + engines/sci/sfx/timer/pthread.c | 104 - engines/sci/sfx/timer/pthread.cpp | 104 + engines/sci/sfx/timer/sigalrm.c | 157 - engines/sci/sfx/timer/sigalrm.cpp | 157 + engines/sci/sfx/timer/timers.c | 73 - engines/sci/sfx/timer/timers.cpp | 73 + engines/sci/sfx/timetest.c | 59 - engines/sci/sfx/timetest.cpp | 59 + engines/sci/tools/bdf.c | 7252 -------------------------- engines/sci/tools/bdf.cpp | 7252 ++++++++++++++++++++++++++ engines/sci/tools/bdfgname.c | 431 -- engines/sci/tools/bdfgname.cpp | 431 ++ engines/sci/tools/bdfgrid.c | 3361 ------------ engines/sci/tools/bdfgrid.cpp | 3361 ++++++++++++ engines/sci/tools/bdftofont.c | 181 - engines/sci/tools/bdftofont.cpp | 181 + engines/sci/tools/classes.c | 54 - engines/sci/tools/classes.cpp | 54 + engines/sci/tools/fonttoc.c | 263 - engines/sci/tools/fonttoc.cpp | 263 + engines/sci/tools/listwords.c | 91 - engines/sci/tools/listwords.cpp | 91 + engines/sci/tools/musicplayer.c | 115 - engines/sci/tools/musicplayer.cpp | 115 + engines/sci/tools/scidisasm.c | 1009 ---- engines/sci/tools/scidisasm.cpp | 1009 ++++ engines/sci/tools/scipack.c | 217 - engines/sci/tools/scipack.cpp | 217 + engines/sci/tools/sciunpack.c | 482 -- engines/sci/tools/sciunpack.cpp | 482 ++ engines/sci/tools/scriptdump.c | 49 - engines/sci/tools/scriptdump.cpp | 49 + engines/sci/tools/vocabdump.c | 77 - engines/sci/tools/vocabdump.cpp | 77 + 267 files changed, 91267 insertions(+), 92176 deletions(-) delete mode 100644 engines/sci/engine/game.c create mode 100644 engines/sci/engine/game.cpp delete mode 100644 engines/sci/engine/gc.c create mode 100644 engines/sci/engine/gc.cpp delete mode 100644 engines/sci/engine/grammar.c create mode 100644 engines/sci/engine/grammar.cpp delete mode 100644 engines/sci/engine/heap.c create mode 100644 engines/sci/engine/heap.cpp delete mode 100644 engines/sci/engine/kernel.c create mode 100644 engines/sci/engine/kernel.cpp delete mode 100644 engines/sci/engine/kevent.c create mode 100644 engines/sci/engine/kevent.cpp delete mode 100644 engines/sci/engine/kfile.c create mode 100644 engines/sci/engine/kfile.cpp delete mode 100644 engines/sci/engine/kgraphics.c create mode 100644 engines/sci/engine/kgraphics.cpp delete mode 100644 engines/sci/engine/klists.c create mode 100644 engines/sci/engine/klists.cpp delete mode 100644 engines/sci/engine/kmath.c create mode 100644 engines/sci/engine/kmath.cpp delete mode 100644 engines/sci/engine/kmenu.c create mode 100644 engines/sci/engine/kmenu.cpp delete mode 100644 engines/sci/engine/kmovement.c create mode 100644 engines/sci/engine/kmovement.cpp delete mode 100644 engines/sci/engine/kpathing.c create mode 100644 engines/sci/engine/kpathing.cpp delete mode 100644 engines/sci/engine/kscripts.c create mode 100644 engines/sci/engine/kscripts.cpp delete mode 100644 engines/sci/engine/ksound.c create mode 100644 engines/sci/engine/ksound.cpp delete mode 100644 engines/sci/engine/kstring.c create mode 100644 engines/sci/engine/kstring.cpp delete mode 100644 engines/sci/engine/makefile.dos delete mode 100644 engines/sci/engine/message.c create mode 100644 engines/sci/engine/message.cpp delete mode 100644 engines/sci/engine/said.c create mode 100644 engines/sci/engine/said.cpp delete mode 100644 engines/sci/engine/savegame.c create mode 100644 engines/sci/engine/savegame.cpp delete mode 100644 engines/sci/engine/scriptconsole.c create mode 100644 engines/sci/engine/scriptconsole.cpp delete mode 100644 engines/sci/engine/scriptdebug.c create mode 100644 engines/sci/engine/scriptdebug.cpp delete mode 100644 engines/sci/engine/seg_manager.c create mode 100644 engines/sci/engine/seg_manager.cpp delete mode 100644 engines/sci/engine/sys_strings.c create mode 100644 engines/sci/engine/sys_strings.cpp delete mode 100644 engines/sci/engine/vm.c create mode 100644 engines/sci/engine/vm.cpp delete mode 100644 engines/sci/gfx/alpha_mvi_crossblit.c create mode 100644 engines/sci/gfx/alpha_mvi_crossblit.cpp delete mode 100644 engines/sci/gfx/antialias.c create mode 100644 engines/sci/gfx/antialias.cpp delete mode 100644 engines/sci/gfx/font-5x8.c create mode 100644 engines/sci/gfx/font-5x8.cpp delete mode 100644 engines/sci/gfx/font-6x10.c create mode 100644 engines/sci/gfx/font-6x10.cpp delete mode 100644 engines/sci/gfx/font.c create mode 100644 engines/sci/gfx/font.cpp delete mode 100644 engines/sci/gfx/gfx_crossblit.c create mode 100644 engines/sci/gfx/gfx_crossblit.cpp delete mode 100644 engines/sci/gfx/gfx_line.c create mode 100644 engines/sci/gfx/gfx_line.cpp delete mode 100644 engines/sci/gfx/gfx_pixmap_scale.c create mode 100644 engines/sci/gfx/gfx_pixmap_scale.cpp delete mode 100644 engines/sci/gfx/gfx_res_options.c create mode 100644 engines/sci/gfx/gfx_res_options.cpp delete mode 100644 engines/sci/gfx/gfx_resource.c create mode 100644 engines/sci/gfx/gfx_resource.cpp delete mode 100644 engines/sci/gfx/gfx_support.c create mode 100644 engines/sci/gfx/gfx_support.cpp delete mode 100644 engines/sci/gfx/gfx_test.c create mode 100644 engines/sci/gfx/gfx_test.cpp delete mode 100644 engines/sci/gfx/gfx_tools.c create mode 100644 engines/sci/gfx/gfx_tools.cpp delete mode 100644 engines/sci/gfx/menubar.c create mode 100644 engines/sci/gfx/menubar.cpp delete mode 100644 engines/sci/gfx/operations.c create mode 100644 engines/sci/gfx/operations.cpp delete mode 100644 engines/sci/gfx/resmgr.c create mode 100644 engines/sci/gfx/resmgr.cpp delete mode 100644 engines/sci/gfx/resource/sci_cursor_0.c create mode 100644 engines/sci/gfx/resource/sci_cursor_0.cpp delete mode 100644 engines/sci/gfx/resource/sci_font.c create mode 100644 engines/sci/gfx/resource/sci_font.cpp delete mode 100644 engines/sci/gfx/resource/sci_pal_1.c create mode 100644 engines/sci/gfx/resource/sci_pal_1.cpp delete mode 100644 engines/sci/gfx/resource/sci_pic_0.c create mode 100644 engines/sci/gfx/resource/sci_pic_0.cpp delete mode 100644 engines/sci/gfx/resource/sci_picfill.c create mode 100644 engines/sci/gfx/resource/sci_picfill.cpp delete mode 100644 engines/sci/gfx/resource/sci_picfill_aux.c create mode 100644 engines/sci/gfx/resource/sci_picfill_aux.cpp delete mode 100644 engines/sci/gfx/resource/sci_resmgr.c create mode 100644 engines/sci/gfx/resource/sci_resmgr.cpp delete mode 100644 engines/sci/gfx/resource/sci_view_0.c create mode 100644 engines/sci/gfx/resource/sci_view_0.cpp delete mode 100644 engines/sci/gfx/resource/sci_view_1.c create mode 100644 engines/sci/gfx/resource/sci_view_1.cpp delete mode 100644 engines/sci/gfx/sbtree.c create mode 100644 engines/sci/gfx/sbtree.cpp delete mode 100644 engines/sci/gfx/sci_widgets.c create mode 100644 engines/sci/gfx/sci_widgets.cpp delete mode 100644 engines/sci/gfx/widgets.c create mode 100644 engines/sci/gfx/widgets.cpp delete mode 100644 engines/sci/gfx/wrapper.c delete mode 100644 engines/sci/menu/game_select_init.c delete mode 100644 engines/sci/menu/game_select_screen.c delete mode 100644 engines/sci/scicore/aatree.c create mode 100644 engines/sci/scicore/aatree.cpp delete mode 100644 engines/sci/scicore/console.c create mode 100644 engines/sci/scicore/console.cpp delete mode 100644 engines/sci/scicore/decompress0.c create mode 100644 engines/sci/scicore/decompress0.cpp delete mode 100644 engines/sci/scicore/decompress01.c create mode 100644 engines/sci/scicore/decompress01.cpp delete mode 100644 engines/sci/scicore/decompress1.c create mode 100644 engines/sci/scicore/decompress1.cpp delete mode 100644 engines/sci/scicore/decompress11.c create mode 100644 engines/sci/scicore/decompress11.cpp delete mode 100644 engines/sci/scicore/exe.c create mode 100644 engines/sci/scicore/exe.cpp delete mode 100644 engines/sci/scicore/exe_lzexe.c create mode 100644 engines/sci/scicore/exe_lzexe.cpp delete mode 100644 engines/sci/scicore/exe_raw.c create mode 100644 engines/sci/scicore/exe_raw.cpp delete mode 100644 engines/sci/scicore/fnmatch.c create mode 100644 engines/sci/scicore/fnmatch.cpp delete mode 100644 engines/sci/scicore/hashmap.c create mode 100644 engines/sci/scicore/hashmap.cpp delete mode 100644 engines/sci/scicore/int_hashmap.c create mode 100644 engines/sci/scicore/int_hashmap.cpp delete mode 100644 engines/sci/scicore/modules.c create mode 100644 engines/sci/scicore/modules.cpp delete mode 100644 engines/sci/scicore/old_objects.c create mode 100644 engines/sci/scicore/old_objects.cpp delete mode 100644 engines/sci/scicore/reg_t_hashmap.c create mode 100644 engines/sci/scicore/reg_t_hashmap.cpp delete mode 100644 engines/sci/scicore/resource.c create mode 100644 engines/sci/scicore/resource.cpp delete mode 100644 engines/sci/scicore/resource_map.c create mode 100644 engines/sci/scicore/resource_map.cpp delete mode 100644 engines/sci/scicore/resource_patch.c create mode 100644 engines/sci/scicore/resource_patch.cpp delete mode 100644 engines/sci/scicore/resourcecheck.c create mode 100644 engines/sci/scicore/resourcecheck.cpp delete mode 100644 engines/sci/scicore/sci_memory.c create mode 100644 engines/sci/scicore/sci_memory.cpp delete mode 100644 engines/sci/scicore/script.c create mode 100644 engines/sci/scicore/script.cpp delete mode 100644 engines/sci/scicore/tools.c create mode 100644 engines/sci/scicore/tools.cpp delete mode 100644 engines/sci/scicore/versions.c create mode 100644 engines/sci/scicore/versions.cpp delete mode 100644 engines/sci/scicore/vocab.c create mode 100644 engines/sci/scicore/vocab.cpp delete mode 100644 engines/sci/scicore/vocab_debug.c create mode 100644 engines/sci/scicore/vocab_debug.cpp delete mode 100644 engines/sci/sfx/adlib.c create mode 100644 engines/sci/sfx/adlib.cpp delete mode 100644 engines/sci/sfx/core.c create mode 100644 engines/sci/sfx/core.cpp delete mode 100644 engines/sci/sfx/device/alsa-midi.c create mode 100644 engines/sci/sfx/device/alsa-midi.cpp delete mode 100644 engines/sci/sfx/device/camd-midi.c create mode 100644 engines/sci/sfx/device/camd-midi.cpp delete mode 100644 engines/sci/sfx/device/devices.c create mode 100644 engines/sci/sfx/device/devices.cpp delete mode 100644 engines/sci/sfx/device/unixraw-midi.c create mode 100644 engines/sci/sfx/device/unixraw-midi.cpp delete mode 100644 engines/sci/sfx/iterator.c create mode 100644 engines/sci/sfx/iterator.cpp delete mode 100644 engines/sci/sfx/lists/gm_patches.c create mode 100644 engines/sci/sfx/lists/gm_patches.cpp delete mode 100644 engines/sci/sfx/lists/mt32_timbres.c create mode 100644 engines/sci/sfx/lists/mt32_timbres.cpp delete mode 100644 engines/sci/sfx/mixer/dc.c create mode 100644 engines/sci/sfx/mixer/dc.cpp delete mode 100644 engines/sci/sfx/mixer/mixers.c create mode 100644 engines/sci/sfx/mixer/mixers.cpp delete mode 100644 engines/sci/sfx/mixer/soft.c create mode 100644 engines/sci/sfx/mixer/soft.cpp delete mode 100644 engines/sci/sfx/mixer/test.c create mode 100644 engines/sci/sfx/mixer/test.cpp delete mode 100644 engines/sci/sfx/pcm-iterator.c create mode 100644 engines/sci/sfx/pcm-iterator.cpp delete mode 100644 engines/sci/sfx/pcm_device/alsa.c create mode 100644 engines/sci/sfx/pcm_device/alsa.cpp delete mode 100644 engines/sci/sfx/pcm_device/audbuf_test.c create mode 100644 engines/sci/sfx/pcm_device/audbuf_test.cpp delete mode 100644 engines/sci/sfx/pcm_device/audiobuf.c create mode 100644 engines/sci/sfx/pcm_device/audiobuf.cpp delete mode 100644 engines/sci/sfx/pcm_device/pcm_devices.c create mode 100644 engines/sci/sfx/pcm_device/pcm_devices.cpp delete mode 100644 engines/sci/sfx/pcm_device/sdl.c create mode 100644 engines/sci/sfx/pcm_device/sdl.cpp delete mode 100644 engines/sci/sfx/player/players.c create mode 100644 engines/sci/sfx/player/players.cpp delete mode 100644 engines/sci/sfx/player/polled.c create mode 100644 engines/sci/sfx/player/polled.cpp delete mode 100644 engines/sci/sfx/player/realtime.c create mode 100644 engines/sci/sfx/player/realtime.cpp delete mode 100644 engines/sci/sfx/seq/gm.c create mode 100644 engines/sci/sfx/seq/gm.cpp delete mode 100644 engines/sci/sfx/seq/instrument-map.c create mode 100644 engines/sci/sfx/seq/instrument-map.cpp delete mode 100644 engines/sci/sfx/seq/map-mt32-to-gm.c create mode 100644 engines/sci/sfx/seq/map-mt32-to-gm.cpp delete mode 100644 engines/sci/sfx/seq/mt32.c create mode 100644 engines/sci/sfx/seq/mt32.cpp delete mode 100644 engines/sci/sfx/seq/oss-adlib.c create mode 100644 engines/sci/sfx/seq/oss-adlib.cpp delete mode 100644 engines/sci/sfx/seq/sequencers.c create mode 100644 engines/sci/sfx/seq/sequencers.cpp delete mode 100644 engines/sci/sfx/softseq/SN76496.c create mode 100644 engines/sci/sfx/softseq/SN76496.cpp delete mode 100644 engines/sci/sfx/softseq/amiga.c create mode 100644 engines/sci/sfx/softseq/amiga.cpp delete mode 100644 engines/sci/sfx/softseq/fluidsynth.c create mode 100644 engines/sci/sfx/softseq/fluidsynth.cpp delete mode 100644 engines/sci/sfx/softseq/opl2.c create mode 100644 engines/sci/sfx/softseq/opl2.cpp delete mode 100644 engines/sci/sfx/softseq/pcspeaker.c create mode 100644 engines/sci/sfx/softseq/pcspeaker.cpp delete mode 100644 engines/sci/sfx/softseq/softsequencers.c create mode 100644 engines/sci/sfx/softseq/softsequencers.cpp delete mode 100644 engines/sci/sfx/songlib.c create mode 100644 engines/sci/sfx/songlib.cpp delete mode 100644 engines/sci/sfx/test-iterator.c create mode 100644 engines/sci/sfx/test-iterator.cpp delete mode 100644 engines/sci/sfx/time.c create mode 100644 engines/sci/sfx/time.cpp delete mode 100644 engines/sci/sfx/timer/pthread.c create mode 100644 engines/sci/sfx/timer/pthread.cpp delete mode 100644 engines/sci/sfx/timer/sigalrm.c create mode 100644 engines/sci/sfx/timer/sigalrm.cpp delete mode 100644 engines/sci/sfx/timer/timers.c create mode 100644 engines/sci/sfx/timer/timers.cpp delete mode 100644 engines/sci/sfx/timetest.c create mode 100644 engines/sci/sfx/timetest.cpp delete mode 100644 engines/sci/tools/bdf.c create mode 100644 engines/sci/tools/bdf.cpp delete mode 100644 engines/sci/tools/bdfgname.c create mode 100644 engines/sci/tools/bdfgname.cpp delete mode 100644 engines/sci/tools/bdfgrid.c create mode 100644 engines/sci/tools/bdfgrid.cpp delete mode 100644 engines/sci/tools/bdftofont.c create mode 100644 engines/sci/tools/bdftofont.cpp delete mode 100644 engines/sci/tools/classes.c create mode 100644 engines/sci/tools/classes.cpp delete mode 100644 engines/sci/tools/fonttoc.c create mode 100644 engines/sci/tools/fonttoc.cpp delete mode 100644 engines/sci/tools/listwords.c create mode 100644 engines/sci/tools/listwords.cpp delete mode 100644 engines/sci/tools/musicplayer.c create mode 100644 engines/sci/tools/musicplayer.cpp delete mode 100644 engines/sci/tools/scidisasm.c create mode 100644 engines/sci/tools/scidisasm.cpp delete mode 100644 engines/sci/tools/scipack.c create mode 100644 engines/sci/tools/scipack.cpp delete mode 100644 engines/sci/tools/sciunpack.c create mode 100644 engines/sci/tools/sciunpack.cpp delete mode 100644 engines/sci/tools/scriptdump.c create mode 100644 engines/sci/tools/scriptdump.cpp delete mode 100644 engines/sci/tools/vocabdump.c create mode 100644 engines/sci/tools/vocabdump.cpp diff --git a/engines/sci/engine/game.c b/engines/sci/engine/game.c deleted file mode 100644 index 63d3fb9e94..0000000000 --- a/engines/sci/engine/game.c +++ /dev/null @@ -1,773 +0,0 @@ -/*************************************************************************** - 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] - -***************************************************************************/ - -#include "sci/include/sciresource.h" -#include "sci/include/engine.h" -#include "sci/include/versions.h" -#include "sci/include/kernel.h" -#include "sci/engine/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/game.cpp b/engines/sci/engine/game.cpp new file mode 100644 index 0000000000..63d3fb9e94 --- /dev/null +++ b/engines/sci/engine/game.cpp @@ -0,0 +1,773 @@ +/*************************************************************************** + 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] + +***************************************************************************/ + +#include "sci/include/sciresource.h" +#include "sci/include/engine.h" +#include "sci/include/versions.h" +#include "sci/include/kernel.h" +#include "sci/engine/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 deleted file mode 100644 index bdad84d7d4..0000000000 --- a/engines/sci/engine/gc.c +++ /dev/null @@ -1,334 +0,0 @@ -/*************************************************************************** - 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 "sci/engine/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.cpp b/engines/sci/engine/gc.cpp new file mode 100644 index 0000000000..bdad84d7d4 --- /dev/null +++ b/engines/sci/engine/gc.cpp @@ -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 "sci/engine/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/grammar.c b/engines/sci/engine/grammar.c deleted file mode 100644 index 7daf735b6d..0000000000 --- a/engines/sci/engine/grammar.c +++ /dev/null @@ -1,720 +0,0 @@ -/************************************************************************** - 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 "sci/include/resource.h" -#include "sci/include/vocabulary.h" -#include "sci/include/console.h" -#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/grammar.cpp b/engines/sci/engine/grammar.cpp new file mode 100644 index 0000000000..7daf735b6d --- /dev/null +++ b/engines/sci/engine/grammar.cpp @@ -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 "sci/include/resource.h" +#include "sci/include/vocabulary.h" +#include "sci/include/console.h" +#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 deleted file mode 100644 index 4b2314ace7..0000000000 --- a/engines/sci/engine/heap.c +++ /dev/null @@ -1,298 +0,0 @@ -#include -#include -#include - -#include "sci/include/engine.h" -#include "sci/include/console.h" -#include "sci/engine/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.cpp b/engines/sci/engine/heap.cpp new file mode 100644 index 0000000000..4b2314ace7 --- /dev/null +++ b/engines/sci/engine/heap.cpp @@ -0,0 +1,298 @@ +#include +#include +#include + +#include "sci/include/engine.h" +#include "sci/include/console.h" +#include "sci/engine/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/kernel.c b/engines/sci/engine/kernel.c deleted file mode 100644 index 7498d9763b..0000000000 --- a/engines/sci/engine/kernel.c +++ /dev/null @@ -1,1093 +0,0 @@ -/*************************************************************************** - 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 "sci/engine/gc.h" -#include "sci/include/sciresource.h" -#include "sci/include/engine.h" -#ifdef _WIN32 -# include -# include -#endif - -#include "sci/include/gfx_operations.h" -#include "sci/engine/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.cpp b/engines/sci/engine/kernel.cpp new file mode 100644 index 0000000000..7498d9763b --- /dev/null +++ b/engines/sci/engine/kernel.cpp @@ -0,0 +1,1093 @@ +/*************************************************************************** + 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 "sci/engine/gc.h" +#include "sci/include/sciresource.h" +#include "sci/include/engine.h" +#ifdef _WIN32 +# include +# include +#endif + +#include "sci/include/gfx_operations.h" +#include "sci/engine/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/kevent.c b/engines/sci/engine/kevent.c deleted file mode 100644 index 3b6df771e7..0000000000 --- a/engines/sci/engine/kevent.c +++ /dev/null @@ -1,229 +0,0 @@ -/*************************************************************************** - 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 "sci/include/engine.h" - -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/kevent.cpp b/engines/sci/engine/kevent.cpp new file mode 100644 index 0000000000..3b6df771e7 --- /dev/null +++ b/engines/sci/engine/kevent.cpp @@ -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 "sci/include/engine.h" + +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 deleted file mode 100644 index 629a1a6f00..0000000000 --- a/engines/sci/engine/kfile.c +++ /dev/null @@ -1,1175 +0,0 @@ -/*************************************************************************** - 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 "sci/include/engine.h" - - -#ifdef _WIN32 -#define MAX_PATHLEN MAXPATHLEN -# ifndef PATH_MAX -# define PATH_MAX 255 -# endif -# define WIN32_LEAN_AND_MEAN -# include -# include -# include -#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 [MAXPATHLEN], dir_buffer2 [MAX_PATHLEN]; - 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[MAXPATHLEN + 1]; - getcwd(cpath, MAXPATHLEN + 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/kfile.cpp b/engines/sci/engine/kfile.cpp new file mode 100644 index 0000000000..629a1a6f00 --- /dev/null +++ b/engines/sci/engine/kfile.cpp @@ -0,0 +1,1175 @@ +/*************************************************************************** + 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 "sci/include/engine.h" + + +#ifdef _WIN32 +#define MAX_PATHLEN MAXPATHLEN +# ifndef PATH_MAX +# define PATH_MAX 255 +# endif +# define WIN32_LEAN_AND_MEAN +# include +# include +# include +#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 [MAXPATHLEN], dir_buffer2 [MAX_PATHLEN]; + 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[MAXPATHLEN + 1]; + getcwd(cpath, MAXPATHLEN + 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 deleted file mode 100644 index 963d2362b5..0000000000 --- a/engines/sci/engine/kgraphics.c +++ /dev/null @@ -1,3627 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sciresource.h" -#include "sci/include/engine.h" -#include "sci/include/gfx_widgets.h" -#include "sci/engine/sci_graphics.h" -#include "sci/include/sci_widgets.h" - -#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/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp new file mode 100644 index 0000000000..963d2362b5 --- /dev/null +++ b/engines/sci/engine/kgraphics.cpp @@ -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 "sci/include/sciresource.h" +#include "sci/include/engine.h" +#include "sci/include/gfx_widgets.h" +#include "sci/engine/sci_graphics.h" +#include "sci/include/sci_widgets.h" + +#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 deleted file mode 100644 index 96b527d812..0000000000 --- a/engines/sci/engine/klists.c +++ /dev/null @@ -1,608 +0,0 @@ -/*************************************************************************** - 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 "sci/include/engine.h" - -#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/klists.cpp b/engines/sci/engine/klists.cpp new file mode 100644 index 0000000000..96b527d812 --- /dev/null +++ b/engines/sci/engine/klists.cpp @@ -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 "sci/include/engine.h" + +#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 deleted file mode 100644 index 4c8ebb8ec5..0000000000 --- a/engines/sci/engine/kmath.c +++ /dev/null @@ -1,201 +0,0 @@ -/*************************************************************************** - 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 "sci/include/engine.h" - - -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/kmath.cpp b/engines/sci/engine/kmath.cpp new file mode 100644 index 0000000000..4c8ebb8ec5 --- /dev/null +++ b/engines/sci/engine/kmath.cpp @@ -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 "sci/include/engine.h" + + +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 deleted file mode 100644 index bd676213f4..0000000000 --- a/engines/sci/engine/kmenu.c +++ /dev/null @@ -1,533 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sciresource.h" -#include "sci/include/engine.h" -#include "sci/include/sci_widgets.h" - - -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/kmenu.cpp b/engines/sci/engine/kmenu.cpp new file mode 100644 index 0000000000..bd676213f4 --- /dev/null +++ b/engines/sci/engine/kmenu.cpp @@ -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 "sci/include/sciresource.h" +#include "sci/include/engine.h" +#include "sci/include/sci_widgets.h" + + +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 deleted file mode 100644 index 1234a9028a..0000000000 --- a/engines/sci/engine/kmovement.c +++ /dev/null @@ -1,580 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sciresource.h" -#include "sci/include/engine.h" - -/* -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/kmovement.cpp b/engines/sci/engine/kmovement.cpp new file mode 100644 index 0000000000..1234a9028a --- /dev/null +++ b/engines/sci/engine/kmovement.cpp @@ -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 "sci/include/sciresource.h" +#include "sci/include/engine.h" + +/* +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 deleted file mode 100644 index 89273d1137..0000000000 --- a/engines/sci/engine/kpathing.c +++ /dev/null @@ -1,1734 +0,0 @@ -/*************************************************************************** - 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 "sci/include/engine.h" -#include "sci/include/aatree.h" -#include "sci/include/list.h" - -#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 = 0; - 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/kpathing.cpp b/engines/sci/engine/kpathing.cpp new file mode 100644 index 0000000000..89273d1137 --- /dev/null +++ b/engines/sci/engine/kpathing.cpp @@ -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 "sci/include/engine.h" +#include "sci/include/aatree.h" +#include "sci/include/list.h" + +#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 = 0; + 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 deleted file mode 100644 index 02517365c8..0000000000 --- a/engines/sci/engine/kscripts.c +++ /dev/null @@ -1,353 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sciresource.h" -#include "sci/include/engine.h" -#include "sci/engine/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/kscripts.cpp b/engines/sci/engine/kscripts.cpp new file mode 100644 index 0000000000..02517365c8 --- /dev/null +++ b/engines/sci/engine/kscripts.cpp @@ -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 "sci/include/sciresource.h" +#include "sci/include/engine.h" +#include "sci/engine/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 deleted file mode 100644 index ebce947b50..0000000000 --- a/engines/sci/engine/ksound.c +++ /dev/null @@ -1,948 +0,0 @@ -/*************************************************************************** - 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 "sci/include/engine.h" -#include "sci/include/sfx_player.h" - -#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 = 0; - - 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/ksound.cpp b/engines/sci/engine/ksound.cpp new file mode 100644 index 0000000000..ebce947b50 --- /dev/null +++ b/engines/sci/engine/ksound.cpp @@ -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 "sci/include/engine.h" +#include "sci/include/sfx_player.h" + +#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 = 0; + + 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 deleted file mode 100644 index b8e4ec3b74..0000000000 --- a/engines/sci/engine/kstring.c +++ /dev/null @@ -1,802 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sciresource.h" -#include "sci/include/engine.h" -#include "sci/engine/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/kstring.cpp b/engines/sci/engine/kstring.cpp new file mode 100644 index 0000000000..b8e4ec3b74 --- /dev/null +++ b/engines/sci/engine/kstring.cpp @@ -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 "sci/include/sciresource.h" +#include "sci/include/engine.h" +#include "sci/engine/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 deleted file mode 100644 index 0d4c5fac8e..0000000000 --- a/engines/sci/engine/makefile.dos +++ /dev/null @@ -1,22 +0,0 @@ -# -# 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 deleted file mode 100644 index bf4657a96e..0000000000 --- a/engines/sci/engine/message.c +++ /dev/null @@ -1,226 +0,0 @@ -/*************************************************************************** - 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 "sci/engine/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.cpp b/engines/sci/engine/message.cpp new file mode 100644 index 0000000000..bf4657a96e --- /dev/null +++ b/engines/sci/engine/message.cpp @@ -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 "sci/engine/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/said.c b/engines/sci/engine/said.c deleted file mode 100644 index 5296fc4b51..0000000000 --- a/engines/sci/engine/said.c +++ /dev/null @@ -1,2561 +0,0 @@ -/* 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 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - - -#include "sci/include/engine.h" - -#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 "y.tab.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 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = said_top_branch(said_attach_branch((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)]))); } - break; - - case 3: -#line 141 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = SAID_BRANCH_NULL; } - break; - - case 6: -#line 150 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = said_paren(said_value(0x14b, said_value(0xf900, said_terminal(0xf900))), SAID_BRANCH_NULL); } - break; - - case 7: -#line 156 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = SAID_BRANCH_NULL; } - break; - - case 8: -#line 158 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = said_paren(said_value(0x141, said_value(0x149, (yyvsp[(1) - (1)]))), SAID_BRANCH_NULL); } - break; - - case 9: -#line 164 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = said_aug_branch(0x142, 0x14a, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL); } - break; - - case 10: -#line 166 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = SAID_BRANCH_NULL; } - break; - - case 12: -#line 174 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = said_aug_branch(0x143, 0x14a, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL); } - break; - - case 13: -#line 176 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = SAID_BRANCH_NULL; } - break; - - case 15: -#line 184 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = said_paren(said_value(0x141, said_value(0x153, said_terminal((yyvsp[(1) - (1)])))), SAID_BRANCH_NULL); } - break; - - case 16: -#line 189 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = said_aug_branch(0x141, 0x14f, (yyvsp[(1) - (1)]), SAID_BRANCH_NULL); } - break; - - case 17: -#line 191 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = (yyvsp[(1) - (1)]); } - break; - - case 19: -#line 198 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = (yyvsp[(1) - (3)]); } - break; - - case 20: -#line 200 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = said_attach_branch((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } - break; - - case 21: -#line 202 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = said_attach_branch((yyvsp[(1) - (4)]), (yyvsp[(3) - (4)])); } - break; - - case 22: -#line 204 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = said_attach_branch((yyvsp[(1) - (5)]), (yyvsp[(3) - (5)])); } - break; - - case 23: -#line 210 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = said_attach_branch((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)])); } - break; - - case 24: -#line 212 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = (yyvsp[(1) - (1)]); } - break; - - case 25: -#line 214 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = (yyvsp[(1) - (1)]); } - break; - - case 26: -#line 220 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = (yyvsp[(1) - (1)]); } - break; - - case 27: -#line 222 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = said_aug_branch(0x152, 0x144, (yyvsp[(2) - (3)]), SAID_BRANCH_NULL); } - break; - - case 28: -#line 224 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (3)]), (yyvsp[(3) - (3)])); } - break; - - case 30: -#line 232 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL); } - break; - - case 32: -#line 236 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" - { (yyval) = said_aug_branch(0x141, 0x14c, (yyvsp[(2) - (4)]), SAID_BRANCH_NULL); } - break; - - -/* Line 1267 of yacc.c. */ -#line 1647 "y.tab.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 "../../full-svn/scummvm/trunk/engines/sci/engine/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.cpp b/engines/sci/engine/said.cpp new file mode 100644 index 0000000000..5296fc4b51 --- /dev/null +++ b/engines/sci/engine/said.cpp @@ -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 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + + +#include "sci/include/engine.h" + +#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 "y.tab.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 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = said_top_branch(said_attach_branch((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)]))); } + break; + + case 3: +#line 141 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = SAID_BRANCH_NULL; } + break; + + case 6: +#line 150 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = said_paren(said_value(0x14b, said_value(0xf900, said_terminal(0xf900))), SAID_BRANCH_NULL); } + break; + + case 7: +#line 156 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = SAID_BRANCH_NULL; } + break; + + case 8: +#line 158 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = said_paren(said_value(0x141, said_value(0x149, (yyvsp[(1) - (1)]))), SAID_BRANCH_NULL); } + break; + + case 9: +#line 164 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = said_aug_branch(0x142, 0x14a, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL); } + break; + + case 10: +#line 166 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = SAID_BRANCH_NULL; } + break; + + case 12: +#line 174 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = said_aug_branch(0x143, 0x14a, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL); } + break; + + case 13: +#line 176 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = SAID_BRANCH_NULL; } + break; + + case 15: +#line 184 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = said_paren(said_value(0x141, said_value(0x153, said_terminal((yyvsp[(1) - (1)])))), SAID_BRANCH_NULL); } + break; + + case 16: +#line 189 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = said_aug_branch(0x141, 0x14f, (yyvsp[(1) - (1)]), SAID_BRANCH_NULL); } + break; + + case 17: +#line 191 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = (yyvsp[(1) - (1)]); } + break; + + case 19: +#line 198 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = (yyvsp[(1) - (3)]); } + break; + + case 20: +#line 200 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = said_attach_branch((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } + break; + + case 21: +#line 202 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = said_attach_branch((yyvsp[(1) - (4)]), (yyvsp[(3) - (4)])); } + break; + + case 22: +#line 204 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = said_attach_branch((yyvsp[(1) - (5)]), (yyvsp[(3) - (5)])); } + break; + + case 23: +#line 210 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = said_attach_branch((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)])); } + break; + + case 24: +#line 212 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = (yyvsp[(1) - (1)]); } + break; + + case 25: +#line 214 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = (yyvsp[(1) - (1)]); } + break; + + case 26: +#line 220 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = (yyvsp[(1) - (1)]); } + break; + + case 27: +#line 222 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = said_aug_branch(0x152, 0x144, (yyvsp[(2) - (3)]), SAID_BRANCH_NULL); } + break; + + case 28: +#line 224 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (3)]), (yyvsp[(3) - (3)])); } + break; + + case 30: +#line 232 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL); } + break; + + case 32: +#line 236 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/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 "../../full-svn/scummvm/trunk/engines/sci/engine/said.y" + { (yyval) = said_aug_branch(0x141, 0x14c, (yyvsp[(2) - (4)]), SAID_BRANCH_NULL); } + break; + + +/* Line 1267 of yacc.c. */ +#line 1647 "y.tab.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 "../../full-svn/scummvm/trunk/engines/sci/engine/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/savegame.c b/engines/sci/engine/savegame.c deleted file mode 100644 index 920018e394..0000000000 --- a/engines/sci/engine/savegame.c +++ /dev/null @@ -1,5395 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/gfx_operations.h" -#include "sci/include/sfx_engine.h" -#include "sci/include/engine.h" -#include -#include "sci/engine/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 */ - -#line 796 "savegame.cfsml" -#define CFSML_SUCCESS 0 -#define CFSML_FAILURE 1 - -#line 102 "savegame.cfsml" - -#include /* We need va_lists */ -#include "sci/include/sci_memory.h" - -#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 */ -#line 322 "savegame.cfsml" - - 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 */ -#line 379 "savegame.cfsml" - 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) - */ -} -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 431 "savegame.cfsml" -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); - -#line 444 "savegame.cfsml" -static void -_cfsml_write_synonym_t(FILE *fh, synonym_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_synonym_t(FILE *fh, synonym_t* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -int min, max, i; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_sfx_state_t(FILE *fh, sfx_state_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - fprintf(fh, "{\n"); - fprintf(fh, "songlib = "); - write_songlib_t(fh, (songlib_t*) &(save_struc->songlib)); - fprintf(fh, "\n"); - fprintf(fh, "}"); -} - -#line 538 "savegame.cfsml" -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; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_clone_entry_t(FILE *fh, clone_entry_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -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; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_object_t(FILE *fh, object_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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 */ -#line 490 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_object_t(FILE *fh, object_t* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -int min, max, i; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 663 "savegame.cfsml" - if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { - _cfsml_error("Opening brackets expected at line %d\n", *line); - return CFSML_FAILURE; - } -#line 673 "savegame.cfsml" - /* 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; -#line 699 "savegame.cfsml" - done = i = 0; - do { - if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { -#line 707 "savegame.cfsml" - _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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_string(FILE *fh, char ** save_struc) -{ -#line 454 "savegame.cfsml" - if (!(*save_struc)) - fprintf(fh, "\\null\\"); - else { - char *token = _cfsml_mangle_string((char *) *save_struc); - fprintf(fh, "\"%s\"", token); - free(token); - } -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_string(FILE *fh, char ** save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -#line 577 "savegame.cfsml" - - 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; - } -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_menubar_t(FILE *fh, menubar_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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 */ -#line 490 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_menubar_t(FILE *fh, menubar_t* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -int min, max, i; -#line 599 "savegame.cfsml" - 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")) { -#line 663 "savegame.cfsml" - if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { - _cfsml_error("Opening brackets expected at line %d\n", *line); - return CFSML_FAILURE; - } -#line 673 "savegame.cfsml" - /* 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; -#line 699 "savegame.cfsml" - done = i = 0; - do { - if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { -#line 707 "savegame.cfsml" - _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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_size_t(FILE *fh, size_t* save_struc) -{ - fprintf(fh, "%li", (long) *save_struc); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_size_t(FILE *fh, size_t* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -#line 564 "savegame.cfsml" - - *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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_list_entry_t(FILE *fh, list_entry_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -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; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_int_hash_map_t(FILE *fh, int_hash_map_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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; -#line 490 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -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; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 663 "savegame.cfsml" - 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; -#line 699 "savegame.cfsml" - done = i = 0; - do { - if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { -#line 707 "savegame.cfsml" - _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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_gint16(FILE *fh, gint16* save_struc) -{ - fprintf(fh, "%li", (long) *save_struc); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_gint16(FILE *fh, gint16* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -#line 564 "savegame.cfsml" - - *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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_song_t(FILE *fh, song_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_song_t(FILE *fh, song_t* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -int min, max, i; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_menu_item_t(FILE *fh, menu_item_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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; -#line 490 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -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; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 663 "savegame.cfsml" - 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; -#line 699 "savegame.cfsml" - done = i = 0; - do { - if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { -#line 707 "savegame.cfsml" - _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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_node_entry_t(FILE *fh, node_entry_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -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; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_seg_id_t(FILE *fh, seg_id_t* save_struc) -{ - fprintf(fh, "%li", (long) *save_struc); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_seg_id_t(FILE *fh, seg_id_t* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -#line 564 "savegame.cfsml" - - *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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_dynmem_t(FILE *fh, dynmem_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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 */ -#line 490 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_dynmem_t(FILE *fh, dynmem_t* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -int min, max, i; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 663 "savegame.cfsml" - if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { - _cfsml_error("Opening brackets expected at line %d\n", *line); - return CFSML_FAILURE; - } -#line 673 "savegame.cfsml" - /* 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; -#line 699 "savegame.cfsml" - done = i = 0; - do { - if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { -#line 707 "savegame.cfsml" - _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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_local_variables_t(FILE *fh, local_variables_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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 */ -#line 490 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -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; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 663 "savegame.cfsml" - if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { - _cfsml_error("Opening brackets expected at line %d\n", *line); - return CFSML_FAILURE; - } -#line 673 "savegame.cfsml" - /* 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; -#line 699 "savegame.cfsml" - done = i = 0; - do { - if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { -#line 707 "savegame.cfsml" - _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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_state_t(FILE *fh, state_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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 */ -#line 490 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_state_t(FILE *fh, state_t* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -int min, max, i; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 663 "savegame.cfsml" - if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { - _cfsml_error("Opening brackets expected at line %d\n", *line); - return CFSML_FAILURE; - } -#line 673 "savegame.cfsml" - /* 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; -#line 699 "savegame.cfsml" - done = i = 0; - do { - if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { -#line 707 "savegame.cfsml" - _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")) { -#line 749 "savegame.cfsml" - 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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_node_table_t(FILE *fh, node_table_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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 */ -#line 490 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -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; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 663 "savegame.cfsml" - if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { - _cfsml_error("Opening brackets expected at line %d\n", *line); - return CFSML_FAILURE; - } -#line 673 "savegame.cfsml" - /* 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; -#line 699 "savegame.cfsml" - done = i = 0; - do { - if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { -#line 707 "savegame.cfsml" - _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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_sys_strings_t(FILE *fh, sys_strings_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - fprintf(fh, "{\n"); - fprintf(fh, "strings = "); - min = max = SYS_STRINGS_MAX; -#line 490 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -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; -#line 599 "savegame.cfsml" - 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")) { -#line 663 "savegame.cfsml" - 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; -#line 699 "savegame.cfsml" - done = i = 0; - do { - if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { -#line 707 "savegame.cfsml" - _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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_byte(FILE *fh, byte* save_struc) -{ - fprintf(fh, "%li", (long) *save_struc); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_byte(FILE *fh, byte* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -#line 564 "savegame.cfsml" - - *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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_node_t(FILE *fh, node_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_node_t(FILE *fh, node_t* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -int min, max, i; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_list_table_t(FILE *fh, list_table_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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 */ -#line 490 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -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; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 663 "savegame.cfsml" - if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { - _cfsml_error("Opening brackets expected at line %d\n", *line); - return CFSML_FAILURE; - } -#line 673 "savegame.cfsml" - /* 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; -#line 699 "savegame.cfsml" - done = i = 0; - do { - if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { -#line 707 "savegame.cfsml" - _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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_class_t(FILE *fh, class_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_class_t(FILE *fh, class_t* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -int min, max, i; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_song_handle_t(FILE *fh, song_handle_t* save_struc) -{ - fprintf(fh, "%li", (long) *save_struc); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_song_handle_t(FILE *fh, song_handle_t* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -#line 564 "savegame.cfsml" - - *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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_int(FILE *fh, int* save_struc) -{ - fprintf(fh, "%li", (long) *save_struc); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_int(FILE *fh, int* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -#line 564 "savegame.cfsml" - - *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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_menu_t(FILE *fh, menu_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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 */ -#line 490 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_menu_t(FILE *fh, menu_t* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -int min, max, i; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 663 "savegame.cfsml" - if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { - _cfsml_error("Opening brackets expected at line %d\n", *line); - return CFSML_FAILURE; - } -#line 673 "savegame.cfsml" - /* 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; -#line 699 "savegame.cfsml" - done = i = 0; - do { - if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { -#line 707 "savegame.cfsml" - _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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_long(FILE *fh, long* save_struc) -{ - fprintf(fh, "%li", (long) *save_struc); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_long(FILE *fh, long* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -#line 564 "savegame.cfsml" - - *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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_clone_table_t(FILE *fh, clone_table_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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 */ -#line 490 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -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; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 663 "savegame.cfsml" - if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { - _cfsml_error("Opening brackets expected at line %d\n", *line); - return CFSML_FAILURE; - } -#line 673 "savegame.cfsml" - /* 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; -#line 699 "savegame.cfsml" - done = i = 0; - do { - if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { -#line 707 "savegame.cfsml" - _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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_clone_t(FILE *fh, clone_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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 */ -#line 490 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_clone_t(FILE *fh, clone_t* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -int min, max, i; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 663 "savegame.cfsml" - if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { - _cfsml_error("Opening brackets expected at line %d\n", *line); - return CFSML_FAILURE; - } -#line 673 "savegame.cfsml" - /* 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; -#line 699 "savegame.cfsml" - done = i = 0; - do { - if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { -#line 707 "savegame.cfsml" - _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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_list_t(FILE *fh, list_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_list_t(FILE *fh, list_t* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -int min, max, i; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_sys_string_t(FILE *fh, sys_string_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -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; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_script_t(FILE *fh, script_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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 */ -#line 490 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -static int -_cfsml_read_script_t(FILE *fh, script_t* save_struc, char *lastval, int *line, int *hiteof) -{ - char *token; -int min, max, i; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 663 "savegame.cfsml" - if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { - _cfsml_error("Opening brackets expected at line %d\n", *line); - return CFSML_FAILURE; - } -#line 673 "savegame.cfsml" - /* 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; -#line 699 "savegame.cfsml" - done = i = 0; - do { - if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { -#line 707 "savegame.cfsml" - _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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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 -#line 758 "savegame.cfsml" - { - _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; -} - -#line 444 "savegame.cfsml" -static void -_cfsml_write_seg_manager_t(FILE *fh, seg_manager_t* save_struc) -{ - int min, max, i; - -#line 464 "savegame.cfsml" - 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 */ -#line 490 "savegame.cfsml" - 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, "}"); -} - -#line 538 "savegame.cfsml" -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; -#line 599 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 663 "savegame.cfsml" - if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { - _cfsml_error("Opening brackets expected at line %d\n", *line); - return CFSML_FAILURE; - } -#line 673 "savegame.cfsml" - /* 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; -#line 699 "savegame.cfsml" - done = i = 0; - do { - if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { -#line 707 "savegame.cfsml" - _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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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")) { -#line 749 "savegame.cfsml" - 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 -#line 758 "savegame.cfsml" - { - _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 */ -#line 402 "savegame.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); -#line 877 "savegame.cfsml" -/* Auto-generated CFSML data writer code */ - _cfsml_write_song_t(fh, seeker); - fprintf(fh, "\n"); -/* End of auto-generated CFSML data writer code */ -#line 417 "savegame.cfsml" - 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 */ -#line 823 "savegame.cfsml" - { -#line 834 "savegame.cfsml" - int _cfsml_eof = 0, _cfsml_error; - int dummy; -#line 839 "savegame.cfsml" - char *_cfsml_inp = lastval; -#line 847 "savegame.cfsml" - _cfsml_error = read_song_tp(fh, &newsong, _cfsml_inp, &(*line), &_cfsml_eof); -#line 852 "savegame.cfsml" - *hiteof = _cfsml_error; -#line 859 "savegame.cfsml" - 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 */ -#line 440 "savegame.cfsml" - 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) -{ -#line 877 "savegame.cfsml" -/* 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 */ -#line 482 "savegame.cfsml" -} - -void -write_song_tp(FILE *fh, song_t **foo) -{ -#line 877 "savegame.cfsml" -/* Auto-generated CFSML data writer code */ - _cfsml_write_song_t(fh, *foo); - fprintf(fh, "\n"); -/* End of auto-generated CFSML data writer code */ -#line 488 "savegame.cfsml" -} - -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 */ -#line 823 "savegame.cfsml" - { -#line 834 "savegame.cfsml" - int _cfsml_eof = 0, _cfsml_error; - int dummy; -#line 839 "savegame.cfsml" - char *_cfsml_inp = token; -#line 847 "savegame.cfsml" - _cfsml_error = _cfsml_read_song_t(fh, (*foo), _cfsml_inp, &(*line), &_cfsml_eof); -#line 852 "savegame.cfsml" - *hiteof = _cfsml_error; -#line 859 "savegame.cfsml" - 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 */ -#line 501 "savegame.cfsml" - (*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 */ -#line 823 "savegame.cfsml" - { -#line 834 "savegame.cfsml" - int _cfsml_eof = 0, _cfsml_error; - int dummy; -#line 839 "savegame.cfsml" - char *_cfsml_inp = lastval; -#line 847 "savegame.cfsml" - _cfsml_error = _cfsml_read_int_hash_map_t(fh, (*foo), _cfsml_inp, &(*line), &_cfsml_eof); -#line 852 "savegame.cfsml" - *hiteof = _cfsml_error; -#line 859 "savegame.cfsml" - 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 */ -#line 511 "savegame.cfsml" - (*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) - { -#line 877 "savegame.cfsml" -/* 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 */ -#line 527 "savegame.cfsml" - } 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) { - -#line 877 "savegame.cfsml" -/* Auto-generated CFSML data writer code */ - _cfsml_write_menubar_t(fh, (*foo)); - fprintf(fh, "\n"); -/* End of auto-generated CFSML data writer code */ -#line 581 "savegame.cfsml" - - } 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 */ -#line 823 "savegame.cfsml" - { -#line 834 "savegame.cfsml" - int _cfsml_eof = 0, _cfsml_error; - int dummy; -#line 839 "savegame.cfsml" - char *_cfsml_inp = lastval; -#line 847 "savegame.cfsml" - _cfsml_error = _cfsml_read_menubar_t(fh, (*foo), _cfsml_inp, &(*line), &_cfsml_eof); -#line 852 "savegame.cfsml" - *hiteof = _cfsml_error; -#line 859 "savegame.cfsml" - 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 */ -#line 598 "savegame.cfsml" - - } - return *hiteof; -} - -void -write_mem_obj_t(FILE *fh, mem_obj_t *foo) -{ - fprintf(fh, "%s\n", mem_obj_string_names[foo->type].name); -#line 877 "savegame.cfsml" -/* Auto-generated CFSML data writer code */ - _cfsml_write_int(fh, &foo->segmgr_id); - fprintf(fh, "\n"); -/* End of auto-generated CFSML data writer code */ -#line 608 "savegame.cfsml" - switch (foo->type) - { - case MEM_OBJ_SCRIPT: -#line 877 "savegame.cfsml" -/* 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 */ -#line 612 "savegame.cfsml" - break; - case MEM_OBJ_CLONES: -#line 877 "savegame.cfsml" -/* 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 */ -#line 615 "savegame.cfsml" - break; - case MEM_OBJ_LOCALS: -#line 877 "savegame.cfsml" -/* 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 */ -#line 618 "savegame.cfsml" - break; - case MEM_OBJ_SYS_STRINGS: -#line 877 "savegame.cfsml" -/* 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 */ -#line 621 "savegame.cfsml" - break; - case MEM_OBJ_STACK: -#line 877 "savegame.cfsml" -/* 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 */ -#line 624 "savegame.cfsml" - break; - case MEM_OBJ_HUNK: - break; - case MEM_OBJ_LISTS: -#line 877 "savegame.cfsml" -/* 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 */ -#line 629 "savegame.cfsml" - break; - case MEM_OBJ_NODES: -#line 877 "savegame.cfsml" -/* 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 */ -#line 632 "savegame.cfsml" - break; - case MEM_OBJ_DYNMEM: -#line 877 "savegame.cfsml" -/* 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 */ -#line 635 "savegame.cfsml" - 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 */ -#line 823 "savegame.cfsml" - { -#line 834 "savegame.cfsml" - int _cfsml_eof = 0, _cfsml_error; - int dummy; -#line 842 "savegame.cfsml" - char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); - -#line 847 "savegame.cfsml" - _cfsml_error = _cfsml_read_int(fh, &foo->segmgr_id, _cfsml_inp, &(*line), &_cfsml_eof); -#line 852 "savegame.cfsml" - *hiteof = _cfsml_error; -#line 859 "savegame.cfsml" - 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 */ -#line 651 "savegame.cfsml" - switch (foo->type) - { - case MEM_OBJ_SCRIPT: -/* Auto-generated CFSML data reader code */ -#line 823 "savegame.cfsml" - { -#line 834 "savegame.cfsml" - int _cfsml_eof = 0, _cfsml_error; - int dummy; -#line 842 "savegame.cfsml" - char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); - -#line 847 "savegame.cfsml" - _cfsml_error = _cfsml_read_script_t(fh, &foo->data.script, _cfsml_inp, &(*line), &_cfsml_eof); -#line 852 "savegame.cfsml" - *hiteof = _cfsml_error; -#line 859 "savegame.cfsml" - 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 */ -#line 655 "savegame.cfsml" - break; - case MEM_OBJ_CLONES: -/* Auto-generated CFSML data reader code */ -#line 823 "savegame.cfsml" - { -#line 834 "savegame.cfsml" - int _cfsml_eof = 0, _cfsml_error; - int dummy; -#line 842 "savegame.cfsml" - char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); - -#line 847 "savegame.cfsml" - _cfsml_error = _cfsml_read_clone_table_t(fh, &foo->data.clones, _cfsml_inp, &(*line), &_cfsml_eof); -#line 852 "savegame.cfsml" - *hiteof = _cfsml_error; -#line 859 "savegame.cfsml" - 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 */ -#line 658 "savegame.cfsml" - break; - case MEM_OBJ_LOCALS: -/* Auto-generated CFSML data reader code */ -#line 823 "savegame.cfsml" - { -#line 834 "savegame.cfsml" - int _cfsml_eof = 0, _cfsml_error; - int dummy; -#line 842 "savegame.cfsml" - char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); - -#line 847 "savegame.cfsml" - _cfsml_error = _cfsml_read_local_variables_t(fh, &foo->data.locals, _cfsml_inp, &(*line), &_cfsml_eof); -#line 852 "savegame.cfsml" - *hiteof = _cfsml_error; -#line 859 "savegame.cfsml" - 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 */ -#line 661 "savegame.cfsml" - break; - case MEM_OBJ_SYS_STRINGS: -/* Auto-generated CFSML data reader code */ -#line 823 "savegame.cfsml" - { -#line 834 "savegame.cfsml" - int _cfsml_eof = 0, _cfsml_error; - int dummy; -#line 842 "savegame.cfsml" - char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); - -#line 847 "savegame.cfsml" - _cfsml_error = _cfsml_read_sys_strings_t(fh, &foo->data.sys_strings, _cfsml_inp, &(*line), &_cfsml_eof); -#line 852 "savegame.cfsml" - *hiteof = _cfsml_error; -#line 859 "savegame.cfsml" - 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 */ -#line 664 "savegame.cfsml" - break; - case MEM_OBJ_LISTS: -/* Auto-generated CFSML data reader code */ -#line 823 "savegame.cfsml" - { -#line 834 "savegame.cfsml" - int _cfsml_eof = 0, _cfsml_error; - int dummy; -#line 842 "savegame.cfsml" - char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); - -#line 847 "savegame.cfsml" - _cfsml_error = _cfsml_read_list_table_t(fh, &foo->data.lists, _cfsml_inp, &(*line), &_cfsml_eof); -#line 852 "savegame.cfsml" - *hiteof = _cfsml_error; -#line 859 "savegame.cfsml" - 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 */ -#line 667 "savegame.cfsml" - break; - case MEM_OBJ_NODES: -/* Auto-generated CFSML data reader code */ -#line 823 "savegame.cfsml" - { -#line 834 "savegame.cfsml" - int _cfsml_eof = 0, _cfsml_error; - int dummy; -#line 842 "savegame.cfsml" - char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); - -#line 847 "savegame.cfsml" - _cfsml_error = _cfsml_read_node_table_t(fh, &foo->data.nodes, _cfsml_inp, &(*line), &_cfsml_eof); -#line 852 "savegame.cfsml" - *hiteof = _cfsml_error; -#line 859 "savegame.cfsml" - 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 */ -#line 670 "savegame.cfsml" - break; - case MEM_OBJ_STACK: -/* Auto-generated CFSML data reader code */ -#line 823 "savegame.cfsml" - { -#line 834 "savegame.cfsml" - int _cfsml_eof = 0, _cfsml_error; - int dummy; -#line 842 "savegame.cfsml" - char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); - -#line 847 "savegame.cfsml" - _cfsml_error = _cfsml_read_int(fh, &foo->data.stack.nr, _cfsml_inp, &(*line), &_cfsml_eof); -#line 852 "savegame.cfsml" - *hiteof = _cfsml_error; -#line 859 "savegame.cfsml" - 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 */ -#line 673 "savegame.cfsml" - 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 */ -#line 823 "savegame.cfsml" - { -#line 834 "savegame.cfsml" - int _cfsml_eof = 0, _cfsml_error; - int dummy; -#line 842 "savegame.cfsml" - char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); - -#line 847 "savegame.cfsml" - _cfsml_error = _cfsml_read_dynmem_t(fh, &foo->data.dynmem, _cfsml_inp, &(*line), &_cfsml_eof); -#line 852 "savegame.cfsml" - *hiteof = _cfsml_error; -#line 859 "savegame.cfsml" - 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 */ -#line 680 "savegame.cfsml" - break; - } - - return *hiteof; -} - -void -write_mem_obj_tp(FILE *fh, mem_obj_t **foo) -{ - if (*foo) { - -#line 877 "savegame.cfsml" -/* Auto-generated CFSML data writer code */ - write_mem_obj_t(fh, (*foo)); - fprintf(fh, "\n"); -/* End of auto-generated CFSML data writer code */ -#line 692 "savegame.cfsml" - - } 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 */ -#line 823 "savegame.cfsml" - { -#line 834 "savegame.cfsml" - int _cfsml_eof = 0, _cfsml_error; - int dummy; -#line 839 "savegame.cfsml" - char *_cfsml_inp = lastval; -#line 847 "savegame.cfsml" - _cfsml_error = read_mem_obj_t(fh, (*foo), _cfsml_inp, &(*line), &_cfsml_eof); -#line 852 "savegame.cfsml" - *hiteof = _cfsml_error; -#line 859 "savegame.cfsml" - 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 */ -#line 707 "savegame.cfsml" - 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; -#line 877 "savegame.cfsml" -/* Auto-generated CFSML data writer code */ - _cfsml_write_state_t(fh, s); - fprintf(fh, "\n"); -/* End of auto-generated CFSML data writer code */ -#line 774 "savegame.cfsml" -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 */ -#line 823 "savegame.cfsml" - { -#line 826 "savegame.cfsml" - int _cfsml_line_ctr = 0; -#line 831 "savegame.cfsml" - struct _cfsml_pointer_refstruct **_cfsml_myptrrefptr = _cfsml_get_current_refpointer(); -#line 834 "savegame.cfsml" - int _cfsml_eof = 0, _cfsml_error; - int dummy; -#line 842 "savegame.cfsml" - char *_cfsml_inp = _cfsml_get_identifier(fh, &(_cfsml_line_ctr), &_cfsml_eof, &dummy); - -#line 847 "savegame.cfsml" - _cfsml_error = _cfsml_read_state_t(fh, retval, _cfsml_inp, &(_cfsml_line_ctr), &_cfsml_eof); -#line 852 "savegame.cfsml" - read_eof = _cfsml_error; -#line 856 "savegame.cfsml" - _cfsml_free_pointer_references(_cfsml_myptrrefptr, _cfsml_error); -#line 859 "savegame.cfsml" - 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 */ -#line 1117 "savegame.cfsml" - - 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.cpp b/engines/sci/engine/savegame.cpp new file mode 100644 index 0000000000..920018e394 --- /dev/null +++ b/engines/sci/engine/savegame.cpp @@ -0,0 +1,5395 @@ +/*************************************************************************** + 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 "sci/include/sci_memory.h" +#include "sci/include/gfx_operations.h" +#include "sci/include/sfx_engine.h" +#include "sci/include/engine.h" +#include +#include "sci/engine/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 */ + +#line 796 "savegame.cfsml" +#define CFSML_SUCCESS 0 +#define CFSML_FAILURE 1 + +#line 102 "savegame.cfsml" + +#include /* We need va_lists */ +#include "sci/include/sci_memory.h" + +#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 */ +#line 322 "savegame.cfsml" + + 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 */ +#line 379 "savegame.cfsml" + 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) + */ +} +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 431 "savegame.cfsml" +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); + +#line 444 "savegame.cfsml" +static void +_cfsml_write_synonym_t(FILE *fh, synonym_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_synonym_t(FILE *fh, synonym_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_sfx_state_t(FILE *fh, sfx_state_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + fprintf(fh, "{\n"); + fprintf(fh, "songlib = "); + write_songlib_t(fh, (songlib_t*) &(save_struc->songlib)); + fprintf(fh, "\n"); + fprintf(fh, "}"); +} + +#line 538 "savegame.cfsml" +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; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_clone_entry_t(FILE *fh, clone_entry_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +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; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_object_t(FILE *fh, object_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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 */ +#line 490 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_object_t(FILE *fh, object_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 663 "savegame.cfsml" + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } +#line 673 "savegame.cfsml" + /* 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; +#line 699 "savegame.cfsml" + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { +#line 707 "savegame.cfsml" + _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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_string(FILE *fh, char ** save_struc) +{ +#line 454 "savegame.cfsml" + if (!(*save_struc)) + fprintf(fh, "\\null\\"); + else { + char *token = _cfsml_mangle_string((char *) *save_struc); + fprintf(fh, "\"%s\"", token); + free(token); + } +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_string(FILE *fh, char ** save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +#line 577 "savegame.cfsml" + + 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; + } +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_menubar_t(FILE *fh, menubar_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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 */ +#line 490 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_menubar_t(FILE *fh, menubar_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; +#line 599 "savegame.cfsml" + 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")) { +#line 663 "savegame.cfsml" + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } +#line 673 "savegame.cfsml" + /* 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; +#line 699 "savegame.cfsml" + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { +#line 707 "savegame.cfsml" + _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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_size_t(FILE *fh, size_t* save_struc) +{ + fprintf(fh, "%li", (long) *save_struc); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_size_t(FILE *fh, size_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +#line 564 "savegame.cfsml" + + *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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_list_entry_t(FILE *fh, list_entry_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +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; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_int_hash_map_t(FILE *fh, int_hash_map_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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; +#line 490 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +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; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 663 "savegame.cfsml" + 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; +#line 699 "savegame.cfsml" + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { +#line 707 "savegame.cfsml" + _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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_gint16(FILE *fh, gint16* save_struc) +{ + fprintf(fh, "%li", (long) *save_struc); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_gint16(FILE *fh, gint16* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +#line 564 "savegame.cfsml" + + *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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_song_t(FILE *fh, song_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_song_t(FILE *fh, song_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_menu_item_t(FILE *fh, menu_item_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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; +#line 490 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +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; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 663 "savegame.cfsml" + 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; +#line 699 "savegame.cfsml" + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { +#line 707 "savegame.cfsml" + _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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_node_entry_t(FILE *fh, node_entry_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +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; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_seg_id_t(FILE *fh, seg_id_t* save_struc) +{ + fprintf(fh, "%li", (long) *save_struc); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_seg_id_t(FILE *fh, seg_id_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +#line 564 "savegame.cfsml" + + *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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_dynmem_t(FILE *fh, dynmem_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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 */ +#line 490 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_dynmem_t(FILE *fh, dynmem_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 663 "savegame.cfsml" + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } +#line 673 "savegame.cfsml" + /* 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; +#line 699 "savegame.cfsml" + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { +#line 707 "savegame.cfsml" + _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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_local_variables_t(FILE *fh, local_variables_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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 */ +#line 490 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +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; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 663 "savegame.cfsml" + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } +#line 673 "savegame.cfsml" + /* 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; +#line 699 "savegame.cfsml" + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { +#line 707 "savegame.cfsml" + _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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_state_t(FILE *fh, state_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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 */ +#line 490 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_state_t(FILE *fh, state_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 663 "savegame.cfsml" + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } +#line 673 "savegame.cfsml" + /* 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; +#line 699 "savegame.cfsml" + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { +#line 707 "savegame.cfsml" + _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")) { +#line 749 "savegame.cfsml" + 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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_node_table_t(FILE *fh, node_table_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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 */ +#line 490 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +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; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 663 "savegame.cfsml" + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } +#line 673 "savegame.cfsml" + /* 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; +#line 699 "savegame.cfsml" + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { +#line 707 "savegame.cfsml" + _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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_sys_strings_t(FILE *fh, sys_strings_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + fprintf(fh, "{\n"); + fprintf(fh, "strings = "); + min = max = SYS_STRINGS_MAX; +#line 490 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +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; +#line 599 "savegame.cfsml" + 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")) { +#line 663 "savegame.cfsml" + 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; +#line 699 "savegame.cfsml" + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { +#line 707 "savegame.cfsml" + _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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_byte(FILE *fh, byte* save_struc) +{ + fprintf(fh, "%li", (long) *save_struc); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_byte(FILE *fh, byte* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +#line 564 "savegame.cfsml" + + *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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_node_t(FILE *fh, node_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_node_t(FILE *fh, node_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_list_table_t(FILE *fh, list_table_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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 */ +#line 490 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +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; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 663 "savegame.cfsml" + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } +#line 673 "savegame.cfsml" + /* 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; +#line 699 "savegame.cfsml" + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { +#line 707 "savegame.cfsml" + _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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_class_t(FILE *fh, class_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_class_t(FILE *fh, class_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_song_handle_t(FILE *fh, song_handle_t* save_struc) +{ + fprintf(fh, "%li", (long) *save_struc); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_song_handle_t(FILE *fh, song_handle_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +#line 564 "savegame.cfsml" + + *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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_int(FILE *fh, int* save_struc) +{ + fprintf(fh, "%li", (long) *save_struc); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_int(FILE *fh, int* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +#line 564 "savegame.cfsml" + + *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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_menu_t(FILE *fh, menu_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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 */ +#line 490 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_menu_t(FILE *fh, menu_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 663 "savegame.cfsml" + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } +#line 673 "savegame.cfsml" + /* 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; +#line 699 "savegame.cfsml" + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { +#line 707 "savegame.cfsml" + _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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_long(FILE *fh, long* save_struc) +{ + fprintf(fh, "%li", (long) *save_struc); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_long(FILE *fh, long* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +#line 564 "savegame.cfsml" + + *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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_clone_table_t(FILE *fh, clone_table_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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 */ +#line 490 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +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; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 663 "savegame.cfsml" + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } +#line 673 "savegame.cfsml" + /* 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; +#line 699 "savegame.cfsml" + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { +#line 707 "savegame.cfsml" + _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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_clone_t(FILE *fh, clone_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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 */ +#line 490 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_clone_t(FILE *fh, clone_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 663 "savegame.cfsml" + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } +#line 673 "savegame.cfsml" + /* 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; +#line 699 "savegame.cfsml" + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { +#line 707 "savegame.cfsml" + _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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_list_t(FILE *fh, list_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_list_t(FILE *fh, list_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_sys_string_t(FILE *fh, sys_string_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +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; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_script_t(FILE *fh, script_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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 */ +#line 490 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +static int +_cfsml_read_script_t(FILE *fh, script_t* save_struc, char *lastval, int *line, int *hiteof) +{ + char *token; +int min, max, i; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 663 "savegame.cfsml" + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } +#line 673 "savegame.cfsml" + /* 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; +#line 699 "savegame.cfsml" + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { +#line 707 "savegame.cfsml" + _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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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 +#line 758 "savegame.cfsml" + { + _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; +} + +#line 444 "savegame.cfsml" +static void +_cfsml_write_seg_manager_t(FILE *fh, seg_manager_t* save_struc) +{ + int min, max, i; + +#line 464 "savegame.cfsml" + 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 */ +#line 490 "savegame.cfsml" + 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, "}"); +} + +#line 538 "savegame.cfsml" +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; +#line 599 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 663 "savegame.cfsml" + if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) { + _cfsml_error("Opening brackets expected at line %d\n", *line); + return CFSML_FAILURE; + } +#line 673 "savegame.cfsml" + /* 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; +#line 699 "savegame.cfsml" + done = i = 0; + do { + if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) { +#line 707 "savegame.cfsml" + _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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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")) { +#line 749 "savegame.cfsml" + 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 +#line 758 "savegame.cfsml" + { + _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 */ +#line 402 "savegame.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); +#line 877 "savegame.cfsml" +/* Auto-generated CFSML data writer code */ + _cfsml_write_song_t(fh, seeker); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ +#line 417 "savegame.cfsml" + 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 */ +#line 823 "savegame.cfsml" + { +#line 834 "savegame.cfsml" + int _cfsml_eof = 0, _cfsml_error; + int dummy; +#line 839 "savegame.cfsml" + char *_cfsml_inp = lastval; +#line 847 "savegame.cfsml" + _cfsml_error = read_song_tp(fh, &newsong, _cfsml_inp, &(*line), &_cfsml_eof); +#line 852 "savegame.cfsml" + *hiteof = _cfsml_error; +#line 859 "savegame.cfsml" + 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 */ +#line 440 "savegame.cfsml" + 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) +{ +#line 877 "savegame.cfsml" +/* 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 */ +#line 482 "savegame.cfsml" +} + +void +write_song_tp(FILE *fh, song_t **foo) +{ +#line 877 "savegame.cfsml" +/* Auto-generated CFSML data writer code */ + _cfsml_write_song_t(fh, *foo); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ +#line 488 "savegame.cfsml" +} + +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 */ +#line 823 "savegame.cfsml" + { +#line 834 "savegame.cfsml" + int _cfsml_eof = 0, _cfsml_error; + int dummy; +#line 839 "savegame.cfsml" + char *_cfsml_inp = token; +#line 847 "savegame.cfsml" + _cfsml_error = _cfsml_read_song_t(fh, (*foo), _cfsml_inp, &(*line), &_cfsml_eof); +#line 852 "savegame.cfsml" + *hiteof = _cfsml_error; +#line 859 "savegame.cfsml" + 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 */ +#line 501 "savegame.cfsml" + (*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 */ +#line 823 "savegame.cfsml" + { +#line 834 "savegame.cfsml" + int _cfsml_eof = 0, _cfsml_error; + int dummy; +#line 839 "savegame.cfsml" + char *_cfsml_inp = lastval; +#line 847 "savegame.cfsml" + _cfsml_error = _cfsml_read_int_hash_map_t(fh, (*foo), _cfsml_inp, &(*line), &_cfsml_eof); +#line 852 "savegame.cfsml" + *hiteof = _cfsml_error; +#line 859 "savegame.cfsml" + 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 */ +#line 511 "savegame.cfsml" + (*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) + { +#line 877 "savegame.cfsml" +/* 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 */ +#line 527 "savegame.cfsml" + } 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) { + +#line 877 "savegame.cfsml" +/* Auto-generated CFSML data writer code */ + _cfsml_write_menubar_t(fh, (*foo)); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ +#line 581 "savegame.cfsml" + + } 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 */ +#line 823 "savegame.cfsml" + { +#line 834 "savegame.cfsml" + int _cfsml_eof = 0, _cfsml_error; + int dummy; +#line 839 "savegame.cfsml" + char *_cfsml_inp = lastval; +#line 847 "savegame.cfsml" + _cfsml_error = _cfsml_read_menubar_t(fh, (*foo), _cfsml_inp, &(*line), &_cfsml_eof); +#line 852 "savegame.cfsml" + *hiteof = _cfsml_error; +#line 859 "savegame.cfsml" + 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 */ +#line 598 "savegame.cfsml" + + } + return *hiteof; +} + +void +write_mem_obj_t(FILE *fh, mem_obj_t *foo) +{ + fprintf(fh, "%s\n", mem_obj_string_names[foo->type].name); +#line 877 "savegame.cfsml" +/* Auto-generated CFSML data writer code */ + _cfsml_write_int(fh, &foo->segmgr_id); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ +#line 608 "savegame.cfsml" + switch (foo->type) + { + case MEM_OBJ_SCRIPT: +#line 877 "savegame.cfsml" +/* 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 */ +#line 612 "savegame.cfsml" + break; + case MEM_OBJ_CLONES: +#line 877 "savegame.cfsml" +/* 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 */ +#line 615 "savegame.cfsml" + break; + case MEM_OBJ_LOCALS: +#line 877 "savegame.cfsml" +/* 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 */ +#line 618 "savegame.cfsml" + break; + case MEM_OBJ_SYS_STRINGS: +#line 877 "savegame.cfsml" +/* 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 */ +#line 621 "savegame.cfsml" + break; + case MEM_OBJ_STACK: +#line 877 "savegame.cfsml" +/* 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 */ +#line 624 "savegame.cfsml" + break; + case MEM_OBJ_HUNK: + break; + case MEM_OBJ_LISTS: +#line 877 "savegame.cfsml" +/* 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 */ +#line 629 "savegame.cfsml" + break; + case MEM_OBJ_NODES: +#line 877 "savegame.cfsml" +/* 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 */ +#line 632 "savegame.cfsml" + break; + case MEM_OBJ_DYNMEM: +#line 877 "savegame.cfsml" +/* 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 */ +#line 635 "savegame.cfsml" + 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 */ +#line 823 "savegame.cfsml" + { +#line 834 "savegame.cfsml" + int _cfsml_eof = 0, _cfsml_error; + int dummy; +#line 842 "savegame.cfsml" + char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); + +#line 847 "savegame.cfsml" + _cfsml_error = _cfsml_read_int(fh, &foo->segmgr_id, _cfsml_inp, &(*line), &_cfsml_eof); +#line 852 "savegame.cfsml" + *hiteof = _cfsml_error; +#line 859 "savegame.cfsml" + 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 */ +#line 651 "savegame.cfsml" + switch (foo->type) + { + case MEM_OBJ_SCRIPT: +/* Auto-generated CFSML data reader code */ +#line 823 "savegame.cfsml" + { +#line 834 "savegame.cfsml" + int _cfsml_eof = 0, _cfsml_error; + int dummy; +#line 842 "savegame.cfsml" + char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); + +#line 847 "savegame.cfsml" + _cfsml_error = _cfsml_read_script_t(fh, &foo->data.script, _cfsml_inp, &(*line), &_cfsml_eof); +#line 852 "savegame.cfsml" + *hiteof = _cfsml_error; +#line 859 "savegame.cfsml" + 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 */ +#line 655 "savegame.cfsml" + break; + case MEM_OBJ_CLONES: +/* Auto-generated CFSML data reader code */ +#line 823 "savegame.cfsml" + { +#line 834 "savegame.cfsml" + int _cfsml_eof = 0, _cfsml_error; + int dummy; +#line 842 "savegame.cfsml" + char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); + +#line 847 "savegame.cfsml" + _cfsml_error = _cfsml_read_clone_table_t(fh, &foo->data.clones, _cfsml_inp, &(*line), &_cfsml_eof); +#line 852 "savegame.cfsml" + *hiteof = _cfsml_error; +#line 859 "savegame.cfsml" + 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 */ +#line 658 "savegame.cfsml" + break; + case MEM_OBJ_LOCALS: +/* Auto-generated CFSML data reader code */ +#line 823 "savegame.cfsml" + { +#line 834 "savegame.cfsml" + int _cfsml_eof = 0, _cfsml_error; + int dummy; +#line 842 "savegame.cfsml" + char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); + +#line 847 "savegame.cfsml" + _cfsml_error = _cfsml_read_local_variables_t(fh, &foo->data.locals, _cfsml_inp, &(*line), &_cfsml_eof); +#line 852 "savegame.cfsml" + *hiteof = _cfsml_error; +#line 859 "savegame.cfsml" + 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 */ +#line 661 "savegame.cfsml" + break; + case MEM_OBJ_SYS_STRINGS: +/* Auto-generated CFSML data reader code */ +#line 823 "savegame.cfsml" + { +#line 834 "savegame.cfsml" + int _cfsml_eof = 0, _cfsml_error; + int dummy; +#line 842 "savegame.cfsml" + char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); + +#line 847 "savegame.cfsml" + _cfsml_error = _cfsml_read_sys_strings_t(fh, &foo->data.sys_strings, _cfsml_inp, &(*line), &_cfsml_eof); +#line 852 "savegame.cfsml" + *hiteof = _cfsml_error; +#line 859 "savegame.cfsml" + 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 */ +#line 664 "savegame.cfsml" + break; + case MEM_OBJ_LISTS: +/* Auto-generated CFSML data reader code */ +#line 823 "savegame.cfsml" + { +#line 834 "savegame.cfsml" + int _cfsml_eof = 0, _cfsml_error; + int dummy; +#line 842 "savegame.cfsml" + char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); + +#line 847 "savegame.cfsml" + _cfsml_error = _cfsml_read_list_table_t(fh, &foo->data.lists, _cfsml_inp, &(*line), &_cfsml_eof); +#line 852 "savegame.cfsml" + *hiteof = _cfsml_error; +#line 859 "savegame.cfsml" + 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 */ +#line 667 "savegame.cfsml" + break; + case MEM_OBJ_NODES: +/* Auto-generated CFSML data reader code */ +#line 823 "savegame.cfsml" + { +#line 834 "savegame.cfsml" + int _cfsml_eof = 0, _cfsml_error; + int dummy; +#line 842 "savegame.cfsml" + char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); + +#line 847 "savegame.cfsml" + _cfsml_error = _cfsml_read_node_table_t(fh, &foo->data.nodes, _cfsml_inp, &(*line), &_cfsml_eof); +#line 852 "savegame.cfsml" + *hiteof = _cfsml_error; +#line 859 "savegame.cfsml" + 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 */ +#line 670 "savegame.cfsml" + break; + case MEM_OBJ_STACK: +/* Auto-generated CFSML data reader code */ +#line 823 "savegame.cfsml" + { +#line 834 "savegame.cfsml" + int _cfsml_eof = 0, _cfsml_error; + int dummy; +#line 842 "savegame.cfsml" + char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); + +#line 847 "savegame.cfsml" + _cfsml_error = _cfsml_read_int(fh, &foo->data.stack.nr, _cfsml_inp, &(*line), &_cfsml_eof); +#line 852 "savegame.cfsml" + *hiteof = _cfsml_error; +#line 859 "savegame.cfsml" + 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 */ +#line 673 "savegame.cfsml" + 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 */ +#line 823 "savegame.cfsml" + { +#line 834 "savegame.cfsml" + int _cfsml_eof = 0, _cfsml_error; + int dummy; +#line 842 "savegame.cfsml" + char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy); + +#line 847 "savegame.cfsml" + _cfsml_error = _cfsml_read_dynmem_t(fh, &foo->data.dynmem, _cfsml_inp, &(*line), &_cfsml_eof); +#line 852 "savegame.cfsml" + *hiteof = _cfsml_error; +#line 859 "savegame.cfsml" + 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 */ +#line 680 "savegame.cfsml" + break; + } + + return *hiteof; +} + +void +write_mem_obj_tp(FILE *fh, mem_obj_t **foo) +{ + if (*foo) { + +#line 877 "savegame.cfsml" +/* Auto-generated CFSML data writer code */ + write_mem_obj_t(fh, (*foo)); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ +#line 692 "savegame.cfsml" + + } 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 */ +#line 823 "savegame.cfsml" + { +#line 834 "savegame.cfsml" + int _cfsml_eof = 0, _cfsml_error; + int dummy; +#line 839 "savegame.cfsml" + char *_cfsml_inp = lastval; +#line 847 "savegame.cfsml" + _cfsml_error = read_mem_obj_t(fh, (*foo), _cfsml_inp, &(*line), &_cfsml_eof); +#line 852 "savegame.cfsml" + *hiteof = _cfsml_error; +#line 859 "savegame.cfsml" + 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 */ +#line 707 "savegame.cfsml" + 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; +#line 877 "savegame.cfsml" +/* Auto-generated CFSML data writer code */ + _cfsml_write_state_t(fh, s); + fprintf(fh, "\n"); +/* End of auto-generated CFSML data writer code */ +#line 774 "savegame.cfsml" +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 */ +#line 823 "savegame.cfsml" + { +#line 826 "savegame.cfsml" + int _cfsml_line_ctr = 0; +#line 831 "savegame.cfsml" + struct _cfsml_pointer_refstruct **_cfsml_myptrrefptr = _cfsml_get_current_refpointer(); +#line 834 "savegame.cfsml" + int _cfsml_eof = 0, _cfsml_error; + int dummy; +#line 842 "savegame.cfsml" + char *_cfsml_inp = _cfsml_get_identifier(fh, &(_cfsml_line_ctr), &_cfsml_eof, &dummy); + +#line 847 "savegame.cfsml" + _cfsml_error = _cfsml_read_state_t(fh, retval, _cfsml_inp, &(_cfsml_line_ctr), &_cfsml_eof); +#line 852 "savegame.cfsml" + read_eof = _cfsml_error; +#line 856 "savegame.cfsml" + _cfsml_free_pointer_references(_cfsml_myptrrefptr, _cfsml_error); +#line 859 "savegame.cfsml" + 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 */ +#line 1117 "savegame.cfsml" + + 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/scriptconsole.c b/engines/sci/engine/scriptconsole.c deleted file mode 100644 index 48f1f1695b..0000000000 --- a/engines/sci/engine/scriptconsole.c +++ /dev/null @@ -1,1342 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/engine.h" -#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))) - { - unsigned 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/scriptconsole.cpp b/engines/sci/engine/scriptconsole.cpp new file mode 100644 index 0000000000..48f1f1695b --- /dev/null +++ b/engines/sci/engine/scriptconsole.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/include/engine.h" +#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))) + { + unsigned 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 deleted file mode 100644 index a2a1d2427d..0000000000 --- a/engines/sci/engine/scriptdebug.c +++ /dev/null @@ -1,3904 +0,0 @@ -/*************************************************************************** - 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 "sci/engine/gc.h" -#include "sci/include/sciresource.h" -#include "sci/include/engine.h" -#include "sci/include/console.h" -#include "sci/include/kdebug.h" -#include "sci/include/vocabulary.h" -#include "sci/engine/kernel_types.h" -#include "sci/include/sci_midi.h" -#include "sci/include/sci_widgets.h" -#include "sci/include/reg_t_hashmap.h" - -#ifdef _WIN32 -# include -# include -# include -# 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); -#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 = 0; - 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) -{ - unsigned 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) -{ - unsigned 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; - unsigned 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; - unsigned 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) -{ - unsigned 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/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp new file mode 100644 index 0000000000..a2a1d2427d --- /dev/null +++ b/engines/sci/engine/scriptdebug.cpp @@ -0,0 +1,3904 @@ +/*************************************************************************** + 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 "sci/engine/gc.h" +#include "sci/include/sciresource.h" +#include "sci/include/engine.h" +#include "sci/include/console.h" +#include "sci/include/kdebug.h" +#include "sci/include/vocabulary.h" +#include "sci/engine/kernel_types.h" +#include "sci/include/sci_midi.h" +#include "sci/include/sci_widgets.h" +#include "sci/include/reg_t_hashmap.h" + +#ifdef _WIN32 +# include +# include +# include +# 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); +#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 = 0; + 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) +{ + unsigned 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) +{ + unsigned 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; + unsigned 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; + unsigned 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) +{ + unsigned 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 deleted file mode 100644 index b4a675174a..0000000000 --- a/engines/sci/engine/seg_manager.c +++ /dev/null @@ -1,2063 +0,0 @@ -/*************************************************************************** - 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 "sci/include/seg_manager.h" -#include "sci/include/sciresource.h" -#include "sci/include/versions.h" -#include "sci/include/engine.h" - - -/*#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 = 0; - - 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/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp new file mode 100644 index 0000000000..b4a675174a --- /dev/null +++ b/engines/sci/engine/seg_manager.cpp @@ -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 "sci/include/seg_manager.h" +#include "sci/include/sciresource.h" +#include "sci/include/versions.h" +#include "sci/include/engine.h" + + +/*#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 = 0; + + 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/sys_strings.c b/engines/sci/engine/sys_strings.c deleted file mode 100644 index 5aeb1a64c0..0000000000 --- a/engines/sci/engine/sys_strings.c +++ /dev/null @@ -1,119 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sys_strings.h" -#include -#include "sci/include/sci_memory.h" - -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/sys_strings.cpp b/engines/sci/engine/sys_strings.cpp new file mode 100644 index 0000000000..5aeb1a64c0 --- /dev/null +++ b/engines/sci/engine/sys_strings.cpp @@ -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 "sci/include/sys_strings.h" +#include +#include "sci/include/sci_memory.h" + +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 deleted file mode 100644 index d2831fbd69..0000000000 --- a/engines/sci/engine/vm.c +++ /dev/null @@ -1,2410 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sciresource.h" -#include "sci/include/engine.h" -#include "sci/include/versions.h" -#include "sci/include/kdebug.h" -#include "sci/engine/kernel_types.h" -#include "sci/include/seg_manager.h" -#include "sci/engine/gc.h" -#include "sci/include/sfx_player.h" - -#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/engine/vm.cpp b/engines/sci/engine/vm.cpp new file mode 100644 index 0000000000..d2831fbd69 --- /dev/null +++ b/engines/sci/engine/vm.cpp @@ -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 "sci/include/sciresource.h" +#include "sci/include/engine.h" +#include "sci/include/versions.h" +#include "sci/include/kdebug.h" +#include "sci/engine/kernel_types.h" +#include "sci/include/seg_manager.h" +#include "sci/engine/gc.h" +#include "sci/include/sfx_player.h" + +#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/alpha_mvi_crossblit.c b/engines/sci/gfx/alpha_mvi_crossblit.c deleted file mode 100644 index 6a1e238c2b..0000000000 --- a/engines/sci/gfx/alpha_mvi_crossblit.c +++ /dev/null @@ -1,360 +0,0 @@ -/*************************************************************************** - 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/alpha_mvi_crossblit.cpp b/engines/sci/gfx/alpha_mvi_crossblit.cpp new file mode 100644 index 0000000000..6a1e238c2b --- /dev/null +++ b/engines/sci/gfx/alpha_mvi_crossblit.cpp @@ -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 deleted file mode 100644 index 47e70f09e0..0000000000 --- a/engines/sci/gfx/antialias.c +++ /dev/null @@ -1,163 +0,0 @@ -/*************************************************************************** - 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 "sci/include/gfx_system.h" -#include "sci/include/gfx_tools.h" - -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/antialias.cpp b/engines/sci/gfx/antialias.cpp new file mode 100644 index 0000000000..47e70f09e0 --- /dev/null +++ b/engines/sci/gfx/antialias.cpp @@ -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 "sci/include/gfx_system.h" +#include "sci/include/gfx_tools.h" + +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/font-5x8.c b/engines/sci/gfx/font-5x8.c deleted file mode 100644 index 5234570adc..0000000000 --- a/engines/sci/gfx/font-5x8.c +++ /dev/null @@ -1,2580 +0,0 @@ -/* Auto-generated by bdftofont.c */ - -#include "sci/include/gfx_system.h" - -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-5x8.cpp b/engines/sci/gfx/font-5x8.cpp new file mode 100644 index 0000000000..5234570adc --- /dev/null +++ b/engines/sci/gfx/font-5x8.cpp @@ -0,0 +1,2580 @@ +/* Auto-generated by bdftofont.c */ + +#include "sci/include/gfx_system.h" + +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 deleted file mode 100644 index 6fbf661cbe..0000000000 --- a/engines/sci/gfx/font-6x10.c +++ /dev/null @@ -1,3092 +0,0 @@ -/* Auto-generated by bdftofont.c */ - -#include "sci/include/gfx_system.h" - -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-6x10.cpp b/engines/sci/gfx/font-6x10.cpp new file mode 100644 index 0000000000..6fbf661cbe --- /dev/null +++ b/engines/sci/gfx/font-6x10.cpp @@ -0,0 +1,3092 @@ +/* Auto-generated by bdftofont.c */ + +#include "sci/include/gfx_system.h" + +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 deleted file mode 100644 index c047b78f2e..0000000000 --- a/engines/sci/gfx/font.c +++ /dev/null @@ -1,385 +0,0 @@ -/*************************************************************************** - 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 "sci/include/gfx_system.h" -#include "sci/include/gfx_resource.h" -#include "sci/include/gfx_tools.h" - -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/font.cpp b/engines/sci/gfx/font.cpp new file mode 100644 index 0000000000..c047b78f2e --- /dev/null +++ b/engines/sci/gfx/font.cpp @@ -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 "sci/include/gfx_system.h" +#include "sci/include/gfx_resource.h" +#include "sci/include/gfx_tools.h" + +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_crossblit.c b/engines/sci/gfx/gfx_crossblit.c deleted file mode 100644 index af55a90490..0000000000 --- a/engines/sci/gfx/gfx_crossblit.c +++ /dev/null @@ -1,107 +0,0 @@ -/*************************************************************************** - 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_crossblit.cpp b/engines/sci/gfx/gfx_crossblit.cpp new file mode 100644 index 0000000000..af55a90490 --- /dev/null +++ b/engines/sci/gfx/gfx_crossblit.cpp @@ -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 deleted file mode 100644 index 01b6a8fd2a..0000000000 --- a/engines/sci/gfx/gfx_line.c +++ /dev/null @@ -1,96 +0,0 @@ -/*************************************************************************** - 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_line.cpp b/engines/sci/gfx/gfx_line.cpp new file mode 100644 index 0000000000..01b6a8fd2a --- /dev/null +++ b/engines/sci/gfx/gfx_line.cpp @@ -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 deleted file mode 100644 index 0edd0365c8..0000000000 --- a/engines/sci/gfx/gfx_pixmap_scale.c +++ /dev/null @@ -1,497 +0,0 @@ -/* 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 "sci/include/sci_memory.h" - -#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_pixmap_scale.cpp b/engines/sci/gfx/gfx_pixmap_scale.cpp new file mode 100644 index 0000000000..0edd0365c8 --- /dev/null +++ b/engines/sci/gfx/gfx_pixmap_scale.cpp @@ -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 "sci/include/sci_memory.h" + +#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 deleted file mode 100644 index 0692fece0f..0000000000 --- a/engines/sci/gfx/gfx_res_options.c +++ /dev/null @@ -1,652 +0,0 @@ -/*************************************************************************** - 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 "sci/include/gfx_system.h" -#include "sci/include/gfx_options.h" -#include "sci/include/gfx_resmgr.h" - -#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_res_options.cpp b/engines/sci/gfx/gfx_res_options.cpp new file mode 100644 index 0000000000..0692fece0f --- /dev/null +++ b/engines/sci/gfx/gfx_res_options.cpp @@ -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 "sci/include/gfx_system.h" +#include "sci/include/gfx_options.h" +#include "sci/include/gfx_resmgr.h" + +#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 deleted file mode 100644 index 3c1bf50326..0000000000 --- a/engines/sci/gfx/gfx_resource.c +++ /dev/null @@ -1,434 +0,0 @@ -/*************************************************************************** - 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 "sci/include/gfx_system.h" -#include "sci/include/gfx_resource.h" -#include "sci/include/gfx_tools.h" - -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_resource.cpp b/engines/sci/gfx/gfx_resource.cpp new file mode 100644 index 0000000000..3c1bf50326 --- /dev/null +++ b/engines/sci/gfx/gfx_resource.cpp @@ -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 "sci/include/gfx_system.h" +#include "sci/include/gfx_resource.h" +#include "sci/include/gfx_tools.h" + +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 deleted file mode 100644 index 8a1e86e19f..0000000000 --- a/engines/sci/gfx/gfx_support.c +++ /dev/null @@ -1,450 +0,0 @@ -/*************************************************************************** - 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 "sci/include/gfx_system.h" -#include "sci/include/gfx_tools.h" - -#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_support.cpp b/engines/sci/gfx/gfx_support.cpp new file mode 100644 index 0000000000..8a1e86e19f --- /dev/null +++ b/engines/sci/gfx/gfx_support.cpp @@ -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 "sci/include/gfx_system.h" +#include "sci/include/gfx_tools.h" + +#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 deleted file mode 100644 index 2b5b8f36ec..0000000000 --- a/engines/sci/gfx/gfx_test.c +++ /dev/null @@ -1,1504 +0,0 @@ -/*************************************************************************** - 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_test.cpp b/engines/sci/gfx/gfx_test.cpp new file mode 100644 index 0000000000..2b5b8f36ec --- /dev/null +++ b/engines/sci/gfx/gfx_test.cpp @@ -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 deleted file mode 100644 index b768475f3f..0000000000 --- a/engines/sci/gfx/gfx_tools.c +++ /dev/null @@ -1,465 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/gfx_tools.h" - -/* 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/gfx_tools.cpp b/engines/sci/gfx/gfx_tools.cpp new file mode 100644 index 0000000000..b768475f3f --- /dev/null +++ b/engines/sci/gfx/gfx_tools.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/include/gfx_tools.h" + +/* 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 deleted file mode 100644 index 38feace8ca..0000000000 --- a/engines/sci/gfx/menubar.c +++ /dev/null @@ -1,536 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/engine.h" -#include "sci/include/menubar.h" - -#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/menubar.cpp b/engines/sci/gfx/menubar.cpp new file mode 100644 index 0000000000..38feace8ca --- /dev/null +++ b/engines/sci/gfx/menubar.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/include/engine.h" +#include "sci/include/menubar.h" + +#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 deleted file mode 100644 index 110f52a52c..0000000000 --- a/engines/sci/gfx/operations.c +++ /dev/null @@ -1,2490 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/gfx_operations.h" - -#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/operations.cpp b/engines/sci/gfx/operations.cpp new file mode 100644 index 0000000000..110f52a52c --- /dev/null +++ b/engines/sci/gfx/operations.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/include/gfx_operations.h" + +#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 deleted file mode 100644 index d7893738ba..0000000000 --- a/engines/sci/gfx/resmgr.c +++ /dev/null @@ -1,708 +0,0 @@ -/*************************************************************************** - 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 "sci/include/gfx_resource.h" -#include "sci/include/gfx_tools.h" -#include "sci/include/gfx_driver.h" -#include "sci/include/gfx_resmgr.h" -#include "sci/include/gfx_state_internal.h" - -#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/resmgr.cpp b/engines/sci/gfx/resmgr.cpp new file mode 100644 index 0000000000..d7893738ba --- /dev/null +++ b/engines/sci/gfx/resmgr.cpp @@ -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 "sci/include/gfx_resource.h" +#include "sci/include/gfx_tools.h" +#include "sci/include/gfx_driver.h" +#include "sci/include/gfx_resmgr.h" +#include "sci/include/gfx_state_internal.h" + +#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/sci_cursor_0.c b/engines/sci/gfx/resource/sci_cursor_0.c deleted file mode 100644 index 06a8d2b7aa..0000000000 --- a/engines/sci/gfx/resource/sci_cursor_0.c +++ /dev/null @@ -1,107 +0,0 @@ -/*************************************************************************** - 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 "sci/include/gfx_system.h" -#include "sci/include/gfx_resource.h" -#include "sci/include/gfx_tools.h" - - -#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_cursor_0.cpp b/engines/sci/gfx/resource/sci_cursor_0.cpp new file mode 100644 index 0000000000..06a8d2b7aa --- /dev/null +++ b/engines/sci/gfx/resource/sci_cursor_0.cpp @@ -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 "sci/include/gfx_system.h" +#include "sci/include/gfx_resource.h" +#include "sci/include/gfx_tools.h" + + +#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 deleted file mode 100644 index f1ae4edb19..0000000000 --- a/engines/sci/gfx/resource/sci_font.c +++ /dev/null @@ -1,153 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/gfx_system.h" -#include "sci/include/gfx_resource.h" -#include "sci/include/gfx_tools.h" - - -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_font.cpp b/engines/sci/gfx/resource/sci_font.cpp new file mode 100644 index 0000000000..f1ae4edb19 --- /dev/null +++ b/engines/sci/gfx/resource/sci_font.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/include/gfx_system.h" +#include "sci/include/gfx_resource.h" +#include "sci/include/gfx_tools.h" + + +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 deleted file mode 100644 index 2a127f4bb3..0000000000 --- a/engines/sci/gfx/resource/sci_pal_1.c +++ /dev/null @@ -1,178 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/gfx_system.h" -#include "sci/include/gfx_resource.h" - -#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_pal_1.cpp b/engines/sci/gfx/resource/sci_pal_1.cpp new file mode 100644 index 0000000000..2a127f4bb3 --- /dev/null +++ b/engines/sci/gfx/resource/sci_pal_1.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/include/gfx_system.h" +#include "sci/include/gfx_resource.h" + +#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 deleted file mode 100644 index a55265c7ef..0000000000 --- a/engines/sci/gfx/resource/sci_pic_0.c +++ /dev/null @@ -1,2023 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include -#include -#include -#include "sci/include/gfx_resource.h" -#include "sci/include/gfx_tools.h" - -#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_pic_0.cpp b/engines/sci/gfx/resource/sci_pic_0.cpp new file mode 100644 index 0000000000..a55265c7ef --- /dev/null +++ b/engines/sci/gfx/resource/sci_pic_0.cpp @@ -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 "sci/include/sci_memory.h" +#include +#include +#include +#include "sci/include/gfx_resource.h" +#include "sci/include/gfx_tools.h" + +#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 deleted file mode 100644 index c7ecea08a7..0000000000 --- a/engines/sci/gfx/resource/sci_picfill.c +++ /dev/null @@ -1,429 +0,0 @@ -/*************************************************************************** - 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.cpp b/engines/sci/gfx/resource/sci_picfill.cpp new file mode 100644 index 0000000000..c7ecea08a7 --- /dev/null +++ b/engines/sci/gfx/resource/sci_picfill.cpp @@ -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 deleted file mode 100644 index 70d8a822da..0000000000 --- a/engines/sci/gfx/resource/sci_picfill_aux.c +++ /dev/null @@ -1,205 +0,0 @@ -/*************************************************************************** - 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_picfill_aux.cpp b/engines/sci/gfx/resource/sci_picfill_aux.cpp new file mode 100644 index 0000000000..70d8a822da --- /dev/null +++ b/engines/sci/gfx/resource/sci_picfill_aux.cpp @@ -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 deleted file mode 100644 index fce577f8c5..0000000000 --- a/engines/sci/gfx/resource/sci_resmgr.c +++ /dev/null @@ -1,341 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/sciresource.h" -#include "sci/include/gfx_widgets.h" -#include "sci/include/gfx_resmgr.h" -#include "sci/include/gfx_options.h" - -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_resmgr.cpp b/engines/sci/gfx/resource/sci_resmgr.cpp new file mode 100644 index 0000000000..fce577f8c5 --- /dev/null +++ b/engines/sci/gfx/resource/sci_resmgr.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/include/sciresource.h" +#include "sci/include/gfx_widgets.h" +#include "sci/include/gfx_resmgr.h" +#include "sci/include/gfx_options.h" + +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 deleted file mode 100644 index 0101cac5f7..0000000000 --- a/engines/sci/gfx/resource/sci_view_0.c +++ /dev/null @@ -1,254 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/gfx_system.h" -#include "sci/include/gfx_resource.h" -#include "sci/include/gfx_tools.h" - - -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_0.cpp b/engines/sci/gfx/resource/sci_view_0.cpp new file mode 100644 index 0000000000..0101cac5f7 --- /dev/null +++ b/engines/sci/gfx/resource/sci_view_0.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/include/gfx_system.h" +#include "sci/include/gfx_resource.h" +#include "sci/include/gfx_tools.h" + + +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 deleted file mode 100644 index d747545331..0000000000 --- a/engines/sci/gfx/resource/sci_view_1.c +++ /dev/null @@ -1,554 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/gfx_system.h" -#include "sci/include/gfx_resource.h" -#include "sci/include/gfx_tools.h" - -#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/resource/sci_view_1.cpp b/engines/sci/gfx/resource/sci_view_1.cpp new file mode 100644 index 0000000000..d747545331 --- /dev/null +++ b/engines/sci/gfx/resource/sci_view_1.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/include/gfx_system.h" +#include "sci/include/gfx_resource.h" +#include "sci/include/gfx_tools.h" + +#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 deleted file mode 100644 index 48054d4620..0000000000 --- a/engines/sci/gfx/sbtree.c +++ /dev/null @@ -1,473 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/sbtree.h" -#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/sbtree.cpp b/engines/sci/gfx/sbtree.cpp new file mode 100644 index 0000000000..48054d4620 --- /dev/null +++ b/engines/sci/gfx/sbtree.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/include/sbtree.h" +#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 deleted file mode 100644 index 0df88236d1..0000000000 --- a/engines/sci/gfx/sci_widgets.c +++ /dev/null @@ -1,765 +0,0 @@ -/*************************************************************************** - 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 "sci/include/gfx_operations.h" -#include "sci/include/gfx_widgets.h" -#include "sci/include/engine.h" -#include "sci/include/menubar.h" -#include "sci/include/sci_widgets.h" - -#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/sci_widgets.cpp b/engines/sci/gfx/sci_widgets.cpp new file mode 100644 index 0000000000..0df88236d1 --- /dev/null +++ b/engines/sci/gfx/sci_widgets.cpp @@ -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 "sci/include/gfx_operations.h" +#include "sci/include/gfx_widgets.h" +#include "sci/include/engine.h" +#include "sci/include/menubar.h" +#include "sci/include/sci_widgets.h" + +#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 deleted file mode 100644 index 7516512c29..0000000000 --- a/engines/sci/gfx/widgets.c +++ /dev/null @@ -1,2600 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/gfx_widgets.h" - -#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/widgets.cpp b/engines/sci/gfx/widgets.cpp new file mode 100644 index 0000000000..7516512c29 --- /dev/null +++ b/engines/sci/gfx/widgets.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/include/gfx_widgets.h" + +#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 deleted file mode 100644 index 0bf1097c2a..0000000000 --- a/engines/sci/gfx/wrapper.c +++ /dev/null @@ -1,26 +0,0 @@ -/*************************************************************************** - 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/menu/game_select_init.c b/engines/sci/menu/game_select_init.c deleted file mode 100644 index b538a60b38..0000000000 --- a/engines/sci/menu/game_select_init.c +++ /dev/null @@ -1,275 +0,0 @@ -/*************************************************************************** - 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 deleted file mode 100644 index fe27d597d9..0000000000 --- a/engines/sci/menu/game_select_screen.c +++ /dev/null @@ -1,581 +0,0 @@ -/*************************************************************************** - 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 index 3d2ec96909..4984e6c4e1 100644 --- a/engines/sci/module.mk +++ b/engines/sci/module.mk @@ -95,11 +95,6 @@ MODULE_OBJS = \ CXXFLAGS += -Wno-variadic-macros CPPFLAGS += -DSCUMMVM -# 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/engine/savegame.c: $(srcdir)/engines/sci/engine/savegame.cfsml cat $< | perl $(srcdir)/engines/sci/engine/cfsml.pl -f savegame.cfsml > $@ diff --git a/engines/sci/scicore/aatree.c b/engines/sci/scicore/aatree.c deleted file mode 100644 index 7bbef8c43b..0000000000 --- a/engines/sci/scicore/aatree.c +++ /dev/null @@ -1,181 +0,0 @@ -/*************************************************************************** - 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 "sci/include/aatree.h" - -#include "sci/include/sci_memory.h" - -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/aatree.cpp b/engines/sci/scicore/aatree.cpp new file mode 100644 index 0000000000..7bbef8c43b --- /dev/null +++ b/engines/sci/scicore/aatree.cpp @@ -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 "sci/include/aatree.h" + +#include "sci/include/sci_memory.h" + +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 deleted file mode 100644 index 0aba9bd019..0000000000 --- a/engines/sci/scicore/console.c +++ /dev/null @@ -1,151 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/engine.h" -#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/console.cpp b/engines/sci/scicore/console.cpp new file mode 100644 index 0000000000..0aba9bd019 --- /dev/null +++ b/engines/sci/scicore/console.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/include/engine.h" +#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 deleted file mode 100644 index 6dba2d5299..0000000000 --- a/engines/sci/scicore/decompress0.c +++ /dev/null @@ -1,372 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/sciresource.h" - -/* #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/decompress0.cpp b/engines/sci/scicore/decompress0.cpp new file mode 100644 index 0000000000..6dba2d5299 --- /dev/null +++ b/engines/sci/scicore/decompress0.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/include/sciresource.h" + +/* #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 deleted file mode 100644 index e08e98f95b..0000000000 --- a/engines/sci/scicore/decompress01.c +++ /dev/null @@ -1,655 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/sciresource.h" - -/*************************************************************************** -* 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/decompress01.cpp b/engines/sci/scicore/decompress01.cpp new file mode 100644 index 0000000000..e08e98f95b --- /dev/null +++ b/engines/sci/scicore/decompress01.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/include/sciresource.h" + +/*************************************************************************** +* 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 deleted file mode 100644 index be88d775aa..0000000000 --- a/engines/sci/scicore/decompress1.c +++ /dev/null @@ -1,443 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/sciresource.h" - - - -/* 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/decompress1.cpp b/engines/sci/scicore/decompress1.cpp new file mode 100644 index 0000000000..be88d775aa --- /dev/null +++ b/engines/sci/scicore/decompress1.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/include/sciresource.h" + + + +/* 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 deleted file mode 100644 index 88f3a2795a..0000000000 --- a/engines/sci/scicore/decompress11.c +++ /dev/null @@ -1,167 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/sciresource.h" - -#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/decompress11.cpp b/engines/sci/scicore/decompress11.cpp new file mode 100644 index 0000000000..88f3a2795a --- /dev/null +++ b/engines/sci/scicore/decompress11.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/include/sciresource.h" + +#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 deleted file mode 100644 index 4e97f80ed5..0000000000 --- a/engines/sci/scicore/exe.c +++ /dev/null @@ -1,86 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" - -#include "sci/scicore/exe.h" -#include "sci/scicore/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.cpp b/engines/sci/scicore/exe.cpp new file mode 100644 index 0000000000..4e97f80ed5 --- /dev/null +++ b/engines/sci/scicore/exe.cpp @@ -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 "sci/include/sci_memory.h" + +#include "sci/scicore/exe.h" +#include "sci/scicore/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_lzexe.c b/engines/sci/scicore/exe_lzexe.c deleted file mode 100644 index 4445ed8df9..0000000000 --- a/engines/sci/scicore/exe_lzexe.c +++ /dev/null @@ -1,342 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/scicore/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_lzexe.cpp b/engines/sci/scicore/exe_lzexe.cpp new file mode 100644 index 0000000000..4445ed8df9 --- /dev/null +++ b/engines/sci/scicore/exe_lzexe.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/scicore/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 deleted file mode 100644 index 9a0dc5f742..0000000000 --- a/engines/sci/scicore/exe_raw.c +++ /dev/null @@ -1,73 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" - -struct _exe_handle -{ - FILE *f; -}; - -#include "sci/scicore/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/exe_raw.cpp b/engines/sci/scicore/exe_raw.cpp new file mode 100644 index 0000000000..9a0dc5f742 --- /dev/null +++ b/engines/sci/scicore/exe_raw.cpp @@ -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 "sci/include/sci_memory.h" + +struct _exe_handle +{ + FILE *f; +}; + +#include "sci/scicore/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 deleted file mode 100644 index a3b5a0ad69..0000000000 --- a/engines/sci/scicore/fnmatch.c +++ /dev/null @@ -1,847 +0,0 @@ -/* 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 - -#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/fnmatch.cpp b/engines/sci/scicore/fnmatch.cpp new file mode 100644 index 0000000000..a3b5a0ad69 --- /dev/null +++ b/engines/sci/scicore/fnmatch.cpp @@ -0,0 +1,847 @@ +/* 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 + +#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/hashmap.c b/engines/sci/scicore/hashmap.c deleted file mode 100644 index 1b2b6af5c1..0000000000 --- a/engines/sci/scicore/hashmap.c +++ /dev/null @@ -1,186 +0,0 @@ -/*************************************************************************** - 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/hashmap.cpp b/engines/sci/scicore/hashmap.cpp new file mode 100644 index 0000000000..1b2b6af5c1 --- /dev/null +++ b/engines/sci/scicore/hashmap.cpp @@ -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/int_hashmap.c b/engines/sci/scicore/int_hashmap.c deleted file mode 100644 index d170afd3d5..0000000000 --- a/engines/sci/scicore/int_hashmap.c +++ /dev/null @@ -1,33 +0,0 @@ -/*************************************************************************** - 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 "sci/include/int_hashmap.h" - -#include "sci/scicore/hashmap.c" - -DEFINE_FUNCTIONS(int) diff --git a/engines/sci/scicore/int_hashmap.cpp b/engines/sci/scicore/int_hashmap.cpp new file mode 100644 index 0000000000..d170afd3d5 --- /dev/null +++ b/engines/sci/scicore/int_hashmap.cpp @@ -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 "sci/include/int_hashmap.h" + +#include "sci/scicore/hashmap.c" + +DEFINE_FUNCTIONS(int) diff --git a/engines/sci/scicore/modules.c b/engines/sci/scicore/modules.c deleted file mode 100644 index f63d4e4aa0..0000000000 --- a/engines/sci/scicore/modules.c +++ /dev/null @@ -1,153 +0,0 @@ -/*************************************************************************** - 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/modules.cpp b/engines/sci/scicore/modules.cpp new file mode 100644 index 0000000000..f63d4e4aa0 --- /dev/null +++ b/engines/sci/scicore/modules.cpp @@ -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 deleted file mode 100644 index 8408373537..0000000000 --- a/engines/sci/scicore/old_objects.c +++ /dev/null @@ -1,716 +0,0 @@ -#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 +#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 "sci/include/reg_t_hashmap.h" - -#include "sci/scicore/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/reg_t_hashmap.cpp b/engines/sci/scicore/reg_t_hashmap.cpp new file mode 100644 index 0000000000..bee5a8bcf2 --- /dev/null +++ b/engines/sci/scicore/reg_t_hashmap.cpp @@ -0,0 +1,42 @@ +/*************************************************************************** + 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 "sci/include/reg_t_hashmap.h" + +#include "sci/scicore/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 deleted file mode 100644 index 026bd76834..0000000000 --- a/engines/sci/scicore/resource.c +++ /dev/null @@ -1,951 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/sciresource.h" -#include "sci/include/vocabulary.h" /* 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[MAXPATHLEN]; - 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[MAXPATHLEN]; - 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[MAXPATHLEN]; - - 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.cpp b/engines/sci/scicore/resource.cpp new file mode 100644 index 0000000000..026bd76834 --- /dev/null +++ b/engines/sci/scicore/resource.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/include/sciresource.h" +#include "sci/include/vocabulary.h" /* 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[MAXPATHLEN]; + 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[MAXPATHLEN]; + 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[MAXPATHLEN]; + + 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 deleted file mode 100644 index 48ea54cb3a..0000000000 --- a/engines/sci/scicore/resource_map.c +++ /dev/null @@ -1,515 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" -#include "sci/include/sciresource.h" -#include "sci/include/resource.h" -#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_map.cpp b/engines/sci/scicore/resource_map.cpp new file mode 100644 index 0000000000..48ea54cb3a --- /dev/null +++ b/engines/sci/scicore/resource_map.cpp @@ -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 "sci/include/sci_memory.h" +#include "sci/include/sciresource.h" +#include "sci/include/resource.h" +#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 deleted file mode 100644 index 037b082476..0000000000 --- a/engines/sci/scicore/resource_patch.c +++ /dev/null @@ -1,227 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sciresource.h" -#include "sci/include/sci_memory.h" - -#ifdef _WIN32 -#include -#endif - -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[MAXPATHLEN]; - - 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/resource_patch.cpp b/engines/sci/scicore/resource_patch.cpp new file mode 100644 index 0000000000..037b082476 --- /dev/null +++ b/engines/sci/scicore/resource_patch.cpp @@ -0,0 +1,227 @@ +/*************************************************************************** + 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 "sci/include/sciresource.h" +#include "sci/include/sci_memory.h" + +#ifdef _WIN32 +#include +#endif + +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[MAXPATHLEN]; + + 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 deleted file mode 100644 index f4c731b01f..0000000000 --- a/engines/sci/scicore/resourcecheck.c +++ /dev/null @@ -1,38 +0,0 @@ -/*************************************************************************** - 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/resourcecheck.cpp b/engines/sci/scicore/resourcecheck.cpp new file mode 100644 index 0000000000..f4c731b01f --- /dev/null +++ b/engines/sci/scicore/resourcecheck.cpp @@ -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_memory.c b/engines/sci/scicore/sci_memory.c deleted file mode 100644 index 4d0395d9dd..0000000000 --- a/engines/sci/scicore/sci_memory.c +++ /dev/null @@ -1,311 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" - -/*#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/sci_memory.cpp b/engines/sci/scicore/sci_memory.cpp new file mode 100644 index 0000000000..4d0395d9dd --- /dev/null +++ b/engines/sci/scicore/sci_memory.cpp @@ -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 "sci/include/sci_memory.h" + +/*#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 deleted file mode 100644 index b3d977e5ad..0000000000 --- a/engines/sci/scicore/script.c +++ /dev/null @@ -1,488 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sciresource.h" -#include "sci/include/engine.h" - -/* #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/script.cpp b/engines/sci/scicore/script.cpp new file mode 100644 index 0000000000..b3d977e5ad --- /dev/null +++ b/engines/sci/scicore/script.cpp @@ -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 "sci/include/sciresource.h" +#include "sci/include/engine.h" + +/* #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 deleted file mode 100644 index 8167da33b4..0000000000 --- a/engines/sci/scicore/tools.c +++ /dev/null @@ -1,798 +0,0 @@ -/*************************************************************************** - 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 "sci/include/engine.h" - -#ifdef HAVE_SYS_TIME_H -# include -#endif -#ifdef _MSC_VER -# include -# include -# include -# include -#endif - -#ifdef _WIN32 -# include -# include - -void usleep (long usec); - -# 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); -#endif - -#if !defined(HAVE_FNMATCH) && !defined(_WIN32) -# 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) -/******** 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/tools.cpp b/engines/sci/scicore/tools.cpp new file mode 100644 index 0000000000..8167da33b4 --- /dev/null +++ b/engines/sci/scicore/tools.cpp @@ -0,0 +1,798 @@ +/*************************************************************************** + 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 "sci/include/engine.h" + +#ifdef HAVE_SYS_TIME_H +# include +#endif +#ifdef _MSC_VER +# include +# include +# include +# include +#endif + +#ifdef _WIN32 +# include +# include + +void usleep (long usec); + +# 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); +#endif + +#if !defined(HAVE_FNMATCH) && !defined(_WIN32) +# 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) +/******** 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/versions.c b/engines/sci/scicore/versions.c deleted file mode 100644 index 6b6e1ef0ee..0000000000 --- a/engines/sci/scicore/versions.c +++ /dev/null @@ -1,368 +0,0 @@ -/*************************************************************************** - 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 "sci/include/versions.h" -#include "sci/include/engine.h" -#include "sci/include/resource.h" -#include -#include "sci/scicore/games.h" -#include "sci/scicore/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/versions.cpp b/engines/sci/scicore/versions.cpp new file mode 100644 index 0000000000..6b6e1ef0ee --- /dev/null +++ b/engines/sci/scicore/versions.cpp @@ -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 "sci/include/versions.h" +#include "sci/include/engine.h" +#include "sci/include/resource.h" +#include +#include "sci/scicore/games.h" +#include "sci/scicore/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 deleted file mode 100644 index c712fdf4b5..0000000000 --- a/engines/sci/scicore/vocab.c +++ /dev/null @@ -1,713 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sciresource.h" -#include "sci/include/engine.h" -#include "sci/include/kernel.h" - -#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.cpp b/engines/sci/scicore/vocab.cpp new file mode 100644 index 0000000000..c712fdf4b5 --- /dev/null +++ b/engines/sci/scicore/vocab.cpp @@ -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 "sci/include/sciresource.h" +#include "sci/include/engine.h" +#include "sci/include/kernel.h" + +#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 deleted file mode 100644 index 6e22320ee2..0000000000 --- a/engines/sci/scicore/vocab_debug.c +++ /dev/null @@ -1,421 +0,0 @@ -/* Modified 07/18/99 by Christoph Reichenbach: -** - added vocabulary_free_snames(); -*/ - - -#include -#include "sci/include/engine.h" -#include "sci/include/sciresource.h" - -/* 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/scicore/vocab_debug.cpp b/engines/sci/scicore/vocab_debug.cpp new file mode 100644 index 0000000000..6e22320ee2 --- /dev/null +++ b/engines/sci/scicore/vocab_debug.cpp @@ -0,0 +1,421 @@ +/* Modified 07/18/99 by Christoph Reichenbach: +** - added vocabulary_free_snames(); +*/ + + +#include +#include "sci/include/engine.h" +#include "sci/include/sciresource.h" + +/* 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/sfx/adlib.c b/engines/sci/sfx/adlib.c deleted file mode 100644 index c84a087820..0000000000 --- a/engines/sci/sfx/adlib.c +++ /dev/null @@ -1,66 +0,0 @@ -/*************************************************************************** - 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 "sci/sfx/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.cpp b/engines/sci/sfx/adlib.cpp new file mode 100644 index 0000000000..c84a087820 --- /dev/null +++ b/engines/sci/sfx/adlib.cpp @@ -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 "sci/sfx/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/core.c b/engines/sci/sfx/core.c deleted file mode 100644 index 786f80ab45..0000000000 --- a/engines/sci/sfx/core.c +++ /dev/null @@ -1,938 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sfx_timer.h" -#include "sci/include/sfx_iterator_internal.h" -#include "sci/include/sfx_player.h" -#include "sci/sfx/mixer.h" -#include "sci/include/sci_midi.h" - - -/*#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/core.cpp b/engines/sci/sfx/core.cpp new file mode 100644 index 0000000000..786f80ab45 --- /dev/null +++ b/engines/sci/sfx/core.cpp @@ -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 "sci/include/sfx_timer.h" +#include "sci/include/sfx_iterator_internal.h" +#include "sci/include/sfx_player.h" +#include "sci/sfx/mixer.h" +#include "sci/include/sci_midi.h" + + +/*#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/alsa-midi.c b/engines/sci/sfx/device/alsa-midi.c deleted file mode 100644 index 67ca995916..0000000000 --- a/engines/sci/sfx/device/alsa-midi.c +++ /dev/null @@ -1,227 +0,0 @@ -/*************************************************************************** - 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/alsa-midi.cpp b/engines/sci/sfx/device/alsa-midi.cpp new file mode 100644 index 0000000000..67ca995916 --- /dev/null +++ b/engines/sci/sfx/device/alsa-midi.cpp @@ -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 deleted file mode 100644 index 3af12f196e..0000000000 --- a/engines/sci/sfx/device/camd-midi.c +++ /dev/null @@ -1,161 +0,0 @@ -/*************************************************************************** - 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/camd-midi.cpp b/engines/sci/sfx/device/camd-midi.cpp new file mode 100644 index 0000000000..3af12f196e --- /dev/null +++ b/engines/sci/sfx/device/camd-midi.cpp @@ -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 deleted file mode 100644 index 2e54b1df4f..0000000000 --- a/engines/sci/sfx/device/devices.c +++ /dev/null @@ -1,112 +0,0 @@ -/*************************************************************************** - 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 "sci/include/resource.h" - -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/devices.cpp b/engines/sci/sfx/device/devices.cpp new file mode 100644 index 0000000000..2e54b1df4f --- /dev/null +++ b/engines/sci/sfx/device/devices.cpp @@ -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 "sci/include/resource.h" + +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 deleted file mode 100644 index 69ce3890fc..0000000000 --- a/engines/sci/sfx/device/unixraw-midi.c +++ /dev/null @@ -1,100 +0,0 @@ -/*************************************************************************** - 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/device/unixraw-midi.cpp b/engines/sci/sfx/device/unixraw-midi.cpp new file mode 100644 index 0000000000..69ce3890fc --- /dev/null +++ b/engines/sci/sfx/device/unixraw-midi.cpp @@ -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/iterator.c b/engines/sci/sfx/iterator.c deleted file mode 100644 index 78bbdf816b..0000000000 --- a/engines/sci/sfx/iterator.c +++ /dev/null @@ -1,2113 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sfx_iterator_internal.h" -#include "sci/include/sfx_player.h" -#include "sci/include/resource.h" -#include "sci/include/sci_memory.h" - -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? */ - unsigned 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/iterator.cpp b/engines/sci/sfx/iterator.cpp new file mode 100644 index 0000000000..78bbdf816b --- /dev/null +++ b/engines/sci/sfx/iterator.cpp @@ -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 "sci/include/sfx_iterator_internal.h" +#include "sci/include/sfx_player.h" +#include "sci/include/resource.h" +#include "sci/include/sci_memory.h" + +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? */ + unsigned 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_patches.c b/engines/sci/sfx/lists/gm_patches.c deleted file mode 100644 index b959a87461..0000000000 --- a/engines/sci/sfx/lists/gm_patches.c +++ /dev/null @@ -1,198 +0,0 @@ -/*************************************************************************** - 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/gm_patches.cpp b/engines/sci/sfx/lists/gm_patches.cpp new file mode 100644 index 0000000000..b959a87461 --- /dev/null +++ b/engines/sci/sfx/lists/gm_patches.cpp @@ -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 deleted file mode 100644 index 776ff5dbb7..0000000000 --- a/engines/sci/sfx/lists/mt32_timbres.c +++ /dev/null @@ -1,181 +0,0 @@ -/*************************************************************************** - 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/lists/mt32_timbres.cpp b/engines/sci/sfx/lists/mt32_timbres.cpp new file mode 100644 index 0000000000..776ff5dbb7 --- /dev/null +++ b/engines/sci/sfx/lists/mt32_timbres.cpp @@ -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/dc.c b/engines/sci/sfx/mixer/dc.c deleted file mode 100644 index 52b3ab5cbb..0000000000 --- a/engines/sci/sfx/mixer/dc.c +++ /dev/null @@ -1,329 +0,0 @@ -/*************************************************************************** - 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/dc.cpp b/engines/sci/sfx/mixer/dc.cpp new file mode 100644 index 0000000000..52b3ab5cbb --- /dev/null +++ b/engines/sci/sfx/mixer/dc.cpp @@ -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 deleted file mode 100644 index 997525f173..0000000000 --- a/engines/sci/sfx/mixer/mixers.c +++ /dev/null @@ -1,55 +0,0 @@ -/*************************************************************************** - 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 "sci/include/resource.h" - -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/mixers.cpp b/engines/sci/sfx/mixer/mixers.cpp new file mode 100644 index 0000000000..997525f173 --- /dev/null +++ b/engines/sci/sfx/mixer/mixers.cpp @@ -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 "sci/include/resource.h" + +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 deleted file mode 100644 index d71567a209..0000000000 --- a/engines/sci/sfx/mixer/soft.c +++ /dev/null @@ -1,988 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sci_memory.h" - -/* 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/soft.cpp b/engines/sci/sfx/mixer/soft.cpp new file mode 100644 index 0000000000..d71567a209 --- /dev/null +++ b/engines/sci/sfx/mixer/soft.cpp @@ -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 "sci/include/sci_memory.h" + +/* 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 deleted file mode 100644 index 20b3e952e1..0000000000 --- a/engines/sci/sfx/mixer/test.c +++ /dev/null @@ -1,351 +0,0 @@ -/*************************************************************************** - 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/mixer/test.cpp b/engines/sci/sfx/mixer/test.cpp new file mode 100644 index 0000000000..20b3e952e1 --- /dev/null +++ b/engines/sci/sfx/mixer/test.cpp @@ -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/pcm-iterator.c b/engines/sci/sfx/pcm-iterator.c deleted file mode 100644 index cad4bd50f9..0000000000 --- a/engines/sci/sfx/pcm-iterator.c +++ /dev/null @@ -1,117 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sfx_iterator.h" -#include "sci/include/resource.h" /* for BREAKPOINT */ -#include "sci/include/sci_memory.h" - -#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-iterator.cpp b/engines/sci/sfx/pcm-iterator.cpp new file mode 100644 index 0000000000..cad4bd50f9 --- /dev/null +++ b/engines/sci/sfx/pcm-iterator.cpp @@ -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 "sci/include/sfx_iterator.h" +#include "sci/include/resource.h" /* for BREAKPOINT */ +#include "sci/include/sci_memory.h" + +#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/alsa.c b/engines/sci/sfx/pcm_device/alsa.c deleted file mode 100644 index 26d4c35044..0000000000 --- a/engines/sci/sfx/pcm_device/alsa.c +++ /dev/null @@ -1,387 +0,0 @@ -/*************************************************************************** - 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/alsa.cpp b/engines/sci/sfx/pcm_device/alsa.cpp new file mode 100644 index 0000000000..26d4c35044 --- /dev/null +++ b/engines/sci/sfx/pcm_device/alsa.cpp @@ -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 deleted file mode 100644 index 216aa9079e..0000000000 --- a/engines/sci/sfx/pcm_device/audbuf_test.c +++ /dev/null @@ -1,190 +0,0 @@ -/*************************************************************************** - 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/audbuf_test.cpp b/engines/sci/sfx/pcm_device/audbuf_test.cpp new file mode 100644 index 0000000000..216aa9079e --- /dev/null +++ b/engines/sci/sfx/pcm_device/audbuf_test.cpp @@ -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 deleted file mode 100644 index fa23708ce4..0000000000 --- a/engines/sci/sfx/pcm_device/audiobuf.c +++ /dev/null @@ -1,348 +0,0 @@ -/*************************************************************************** - 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.cpp b/engines/sci/sfx/pcm_device/audiobuf.cpp new file mode 100644 index 0000000000..fa23708ce4 --- /dev/null +++ b/engines/sci/sfx/pcm_device/audiobuf.cpp @@ -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/pcm_devices.c b/engines/sci/sfx/pcm_device/pcm_devices.c deleted file mode 100644 index a29de8896f..0000000000 --- a/engines/sci/sfx/pcm_device/pcm_devices.c +++ /dev/null @@ -1,73 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sfx_pcm.h" -#include "sci/include/resource.h" - -#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/pcm_devices.cpp b/engines/sci/sfx/pcm_device/pcm_devices.cpp new file mode 100644 index 0000000000..a29de8896f --- /dev/null +++ b/engines/sci/sfx/pcm_device/pcm_devices.cpp @@ -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 "sci/include/sfx_pcm.h" +#include "sci/include/resource.h" + +#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/sdl.c b/engines/sci/sfx/pcm_device/sdl.c deleted file mode 100644 index bd125835cb..0000000000 --- a/engines/sci/sfx/pcm_device/sdl.c +++ /dev/null @@ -1,272 +0,0 @@ -/*************************************************************************** - 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/pcm_device/sdl.cpp b/engines/sci/sfx/pcm_device/sdl.cpp new file mode 100644 index 0000000000..bd125835cb --- /dev/null +++ b/engines/sci/sfx/pcm_device/sdl.cpp @@ -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/players.c b/engines/sci/sfx/player/players.c deleted file mode 100644 index eafff46bc3..0000000000 --- a/engines/sci/sfx/player/players.c +++ /dev/null @@ -1,54 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sfx_player.h" - -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/players.cpp b/engines/sci/sfx/player/players.cpp new file mode 100644 index 0000000000..eafff46bc3 --- /dev/null +++ b/engines/sci/sfx/player/players.cpp @@ -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 "sci/include/sfx_player.h" + +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 deleted file mode 100644 index 2a4c23fa97..0000000000 --- a/engines/sci/sfx/player/polled.c +++ /dev/null @@ -1,335 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sfx_player.h" -#include "sci/sfx/softseq.h" -#include "sci/sfx/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/polled.cpp b/engines/sci/sfx/player/polled.cpp new file mode 100644 index 0000000000..2a4c23fa97 --- /dev/null +++ b/engines/sci/sfx/player/polled.cpp @@ -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 "sci/include/sfx_player.h" +#include "sci/sfx/softseq.h" +#include "sci/sfx/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 deleted file mode 100644 index d3fbae5800..0000000000 --- a/engines/sci/sfx/player/realtime.c +++ /dev/null @@ -1,328 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sfx_player.h" -#include "sci/sfx/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/player/realtime.cpp b/engines/sci/sfx/player/realtime.cpp new file mode 100644 index 0000000000..d3fbae5800 --- /dev/null +++ b/engines/sci/sfx/player/realtime.cpp @@ -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 "sci/include/sfx_player.h" +#include "sci/sfx/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/gm.c b/engines/sci/sfx/seq/gm.c deleted file mode 100644 index 4889e76ea8..0000000000 --- a/engines/sci/sfx/seq/gm.c +++ /dev/null @@ -1,185 +0,0 @@ -/*************************************************************************** - 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/gm.cpp b/engines/sci/sfx/seq/gm.cpp new file mode 100644 index 0000000000..4889e76ea8 --- /dev/null +++ b/engines/sci/sfx/seq/gm.cpp @@ -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 deleted file mode 100644 index 0d829a0582..0000000000 --- a/engines/sci/sfx/seq/instrument-map.c +++ /dev/null @@ -1,539 +0,0 @@ -/*************************************************************************** - 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.cpp b/engines/sci/sfx/seq/instrument-map.cpp new file mode 100644 index 0000000000..0d829a0582 --- /dev/null +++ b/engines/sci/sfx/seq/instrument-map.cpp @@ -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/map-mt32-to-gm.c b/engines/sci/sfx/seq/map-mt32-to-gm.c deleted file mode 100644 index bcaf3556f1..0000000000 --- a/engines/sci/sfx/seq/map-mt32-to-gm.c +++ /dev/null @@ -1,813 +0,0 @@ -/*************************************************************************** - 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/map-mt32-to-gm.cpp b/engines/sci/sfx/seq/map-mt32-to-gm.cpp new file mode 100644 index 0000000000..bcaf3556f1 --- /dev/null +++ b/engines/sci/sfx/seq/map-mt32-to-gm.cpp @@ -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 deleted file mode 100644 index b71d474927..0000000000 --- a/engines/sci/sfx/seq/mt32.c +++ /dev/null @@ -1,480 +0,0 @@ -/*************************************************************************** - 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/mt32.cpp b/engines/sci/sfx/seq/mt32.cpp new file mode 100644 index 0000000000..b71d474927 --- /dev/null +++ b/engines/sci/sfx/seq/mt32.cpp @@ -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 deleted file mode 100644 index 0406556f56..0000000000 --- a/engines/sci/sfx/seq/oss-adlib.c +++ /dev/null @@ -1,374 +0,0 @@ -/*************************************************************************** - 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/oss-adlib.cpp b/engines/sci/sfx/seq/oss-adlib.cpp new file mode 100644 index 0000000000..0406556f56 --- /dev/null +++ b/engines/sci/sfx/seq/oss-adlib.cpp @@ -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 deleted file mode 100644 index 3ba7f25a46..0000000000 --- a/engines/sci/sfx/seq/sequencers.c +++ /dev/null @@ -1,68 +0,0 @@ -/*************************************************************************** - 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 "sci/include/resource.h" - -#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/seq/sequencers.cpp b/engines/sci/sfx/seq/sequencers.cpp new file mode 100644 index 0000000000..3ba7f25a46 --- /dev/null +++ b/engines/sci/sfx/seq/sequencers.cpp @@ -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 "sci/include/resource.h" + +#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/softseq/SN76496.c b/engines/sci/sfx/softseq/SN76496.c deleted file mode 100644 index 6ecd72bf9e..0000000000 --- a/engines/sci/sfx/softseq/SN76496.c +++ /dev/null @@ -1,248 +0,0 @@ -/*************************************************************************** - 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 "sci/sfx/softseq.h" -#include "sci/include/sci_midi.h" - -#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/SN76496.cpp b/engines/sci/sfx/softseq/SN76496.cpp new file mode 100644 index 0000000000..6ecd72bf9e --- /dev/null +++ b/engines/sci/sfx/softseq/SN76496.cpp @@ -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 "sci/sfx/softseq.h" +#include "sci/include/sci_midi.h" + +#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 deleted file mode 100644 index 8595899718..0000000000 --- a/engines/sci/sfx/softseq/amiga.c +++ /dev/null @@ -1,658 +0,0 @@ -/*************************************************************************** - 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 "sci/include/resource.h" -#include "sci/include/sci_memory.h" -#include "sci/sfx/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/amiga.cpp b/engines/sci/sfx/softseq/amiga.cpp new file mode 100644 index 0000000000..8595899718 --- /dev/null +++ b/engines/sci/sfx/softseq/amiga.cpp @@ -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 "sci/include/resource.h" +#include "sci/include/sci_memory.h" +#include "sci/sfx/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 deleted file mode 100644 index a44d75e5bd..0000000000 --- a/engines/sci/sfx/softseq/fluidsynth.c +++ /dev/null @@ -1,262 +0,0 @@ -/*************************************************************************** - 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/fluidsynth.cpp b/engines/sci/sfx/softseq/fluidsynth.cpp new file mode 100644 index 0000000000..a44d75e5bd --- /dev/null +++ b/engines/sci/sfx/softseq/fluidsynth.cpp @@ -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/opl2.c b/engines/sci/sfx/softseq/opl2.c deleted file mode 100644 index c19de1388a..0000000000 --- a/engines/sci/sfx/softseq/opl2.c +++ /dev/null @@ -1,718 +0,0 @@ -/*************************************************************************** - 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 "sci/include/resource.h" -#include "sci/include/sfx_iterator.h" -#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 + +#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 "sci/sfx/softseq.h" -#include "sci/include/sci_midi.h" - -#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/pcspeaker.cpp b/engines/sci/sfx/softseq/pcspeaker.cpp new file mode 100644 index 0000000000..727b1f9e5a --- /dev/null +++ b/engines/sci/sfx/softseq/pcspeaker.cpp @@ -0,0 +1,184 @@ +/*************************************************************************** + soft-pcspeaker.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) + +***************************************************************************/ +/* PC speaker software sequencer for FreeSCI */ + +#include "sci/sfx/softseq.h" +#include "sci/include/sci_midi.h" + +#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 deleted file mode 100644 index d0f544190e..0000000000 --- a/engines/sci/sfx/softseq/softsequencers.c +++ /dev/null @@ -1,71 +0,0 @@ -/*************************************************************************** - 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/softseq/softsequencers.cpp b/engines/sci/sfx/softseq/softsequencers.cpp new file mode 100644 index 0000000000..d0f544190e --- /dev/null +++ b/engines/sci/sfx/softseq/softsequencers.cpp @@ -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 deleted file mode 100644 index 57475e82db..0000000000 --- a/engines/sci/sfx/songlib.c +++ /dev/null @@ -1,282 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sfx_engine.h" -#include "sci/include/sci_memory.h" - -#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/songlib.cpp b/engines/sci/sfx/songlib.cpp new file mode 100644 index 0000000000..57475e82db --- /dev/null +++ b/engines/sci/sfx/songlib.cpp @@ -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 "sci/include/sfx_engine.h" +#include "sci/include/sci_memory.h" + +#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 deleted file mode 100644 index 1ecce673f9..0000000000 --- a/engines/sci/sfx/test-iterator.c +++ /dev/null @@ -1,450 +0,0 @@ -/*************************************************************************** - 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/test-iterator.cpp b/engines/sci/sfx/test-iterator.cpp new file mode 100644 index 0000000000..1ecce673f9 --- /dev/null +++ b/engines/sci/sfx/test-iterator.cpp @@ -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/time.c b/engines/sci/sfx/time.c deleted file mode 100644 index 0063b8ef65..0000000000 --- a/engines/sci/sfx/time.c +++ /dev/null @@ -1,131 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sfx_time.h" -#include "sci/include/resource.h" - -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/time.cpp b/engines/sci/sfx/time.cpp new file mode 100644 index 0000000000..0063b8ef65 --- /dev/null +++ b/engines/sci/sfx/time.cpp @@ -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 "sci/include/sfx_time.h" +#include "sci/include/resource.h" + +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/pthread.c b/engines/sci/sfx/timer/pthread.c deleted file mode 100644 index 074adbe08f..0000000000 --- a/engines/sci/sfx/timer/pthread.c +++ /dev/null @@ -1,104 +0,0 @@ -/*************************************************************************** - 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/pthread.cpp b/engines/sci/sfx/timer/pthread.cpp new file mode 100644 index 0000000000..074adbe08f --- /dev/null +++ b/engines/sci/sfx/timer/pthread.cpp @@ -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/sigalrm.c b/engines/sci/sfx/timer/sigalrm.c deleted file mode 100644 index 40cc2872e1..0000000000 --- a/engines/sci/sfx/timer/sigalrm.c +++ /dev/null @@ -1,157 +0,0 @@ -/*************************************************************************** - 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/sigalrm.cpp b/engines/sci/sfx/timer/sigalrm.cpp new file mode 100644 index 0000000000..40cc2872e1 --- /dev/null +++ b/engines/sci/sfx/timer/sigalrm.cpp @@ -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 deleted file mode 100644 index 353ae11ca7..0000000000 --- a/engines/sci/sfx/timer/timers.c +++ /dev/null @@ -1,73 +0,0 @@ -/*************************************************************************** - 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 "sci/include/sfx_timer.h" -#include "sci/include/resource.h" - -#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/timer/timers.cpp b/engines/sci/sfx/timer/timers.cpp new file mode 100644 index 0000000000..353ae11ca7 --- /dev/null +++ b/engines/sci/sfx/timer/timers.cpp @@ -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 "sci/include/sfx_timer.h" +#include "sci/include/resource.h" + +#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 deleted file mode 100644 index 80d9ad1b78..0000000000 --- a/engines/sci/sfx/timetest.c +++ /dev/null @@ -1,59 +0,0 @@ -/*************************************************************************** - 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/sfx/timetest.cpp b/engines/sci/sfx/timetest.cpp new file mode 100644 index 0000000000..80d9ad1b78 --- /dev/null +++ b/engines/sci/sfx/timetest.cpp @@ -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/bdf.c b/engines/sci/tools/bdf.c deleted file mode 100644 index 5d97a51692..0000000000 --- a/engines/sci/tools/bdf.c +++ /dev/null @@ -1,7252 +0,0 @@ -/* - * 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.cpp b/engines/sci/tools/bdf.cpp new file mode 100644 index 0000000000..5d97a51692 --- /dev/null +++ b/engines/sci/tools/bdf.cpp @@ -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/bdfgname.c b/engines/sci/tools/bdfgname.c deleted file mode 100644 index 200202562e..0000000000 --- a/engines/sci/tools/bdfgname.c +++ /dev/null @@ -1,431 +0,0 @@ -/* - * 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/bdfgname.cpp b/engines/sci/tools/bdfgname.cpp new file mode 100644 index 0000000000..200202562e --- /dev/null +++ b/engines/sci/tools/bdfgname.cpp @@ -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 deleted file mode 100644 index 4bbd022d1e..0000000000 --- a/engines/sci/tools/bdfgrid.c +++ /dev/null @@ -1,3361 +0,0 @@ -/* - * 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/bdfgrid.cpp b/engines/sci/tools/bdfgrid.cpp new file mode 100644 index 0000000000..4bbd022d1e --- /dev/null +++ b/engines/sci/tools/bdfgrid.cpp @@ -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 deleted file mode 100644 index 4e7d9ddb65..0000000000 --- a/engines/sci/tools/bdftofont.c +++ /dev/null @@ -1,181 +0,0 @@ -/*************************************************************************** - 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/bdftofont.cpp b/engines/sci/tools/bdftofont.cpp new file mode 100644 index 0000000000..4e7d9ddb65 --- /dev/null +++ b/engines/sci/tools/bdftofont.cpp @@ -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 deleted file mode 100644 index cdb8c93f9f..0000000000 --- a/engines/sci/tools/classes.c +++ /dev/null @@ -1,54 +0,0 @@ -#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 + +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/fonttoc.cpp b/engines/sci/tools/fonttoc.cpp new file mode 100644 index 0000000000..f39f2601e5 --- /dev/null +++ b/engines/sci/tools/fonttoc.cpp @@ -0,0 +1,263 @@ +/*************************************************************************** + 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 + + +#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/listwords.c b/engines/sci/tools/listwords.c deleted file mode 100644 index 8e63aada37..0000000000 --- a/engines/sci/tools/listwords.c +++ /dev/null @@ -1,91 +0,0 @@ -/*************************************************************************** - 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/listwords.cpp b/engines/sci/tools/listwords.cpp new file mode 100644 index 0000000000..8e63aada37 --- /dev/null +++ b/engines/sci/tools/listwords.cpp @@ -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/musicplayer.c b/engines/sci/tools/musicplayer.c deleted file mode 100644 index 34024ddf15..0000000000 --- a/engines/sci/tools/musicplayer.c +++ /dev/null @@ -1,115 +0,0 @@ -/*************************************************************************** - 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/musicplayer.cpp b/engines/sci/tools/musicplayer.cpp new file mode 100644 index 0000000000..34024ddf15 --- /dev/null +++ b/engines/sci/tools/musicplayer.cpp @@ -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 deleted file mode 100644 index dc5e9f8d07..0000000000 --- a/engines/sci/tools/scidisasm.c +++ /dev/null @@ -1,1009 +0,0 @@ -/*************************************************************************** - 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/scidisasm.cpp b/engines/sci/tools/scidisasm.cpp new file mode 100644 index 0000000000..dc5e9f8d07 --- /dev/null +++ b/engines/sci/tools/scidisasm.cpp @@ -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 deleted file mode 100644 index a5b7a8d8a1..0000000000 --- a/engines/sci/tools/scipack.c +++ /dev/null @@ -1,217 +0,0 @@ -/*************************************************************************** - 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/scipack.cpp b/engines/sci/tools/scipack.cpp new file mode 100644 index 0000000000..a5b7a8d8a1 --- /dev/null +++ b/engines/sci/tools/scipack.cpp @@ -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 deleted file mode 100644 index e5b3b39a2e..0000000000 --- a/engines/sci/tools/sciunpack.c +++ /dev/null @@ -1,482 +0,0 @@ -/*************************************************************************** - 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.cpp b/engines/sci/tools/sciunpack.cpp new file mode 100644 index 0000000000..e5b3b39a2e --- /dev/null +++ b/engines/sci/tools/sciunpack.cpp @@ -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/scriptdump.c b/engines/sci/tools/scriptdump.c deleted file mode 100644 index 6efe150ba2..0000000000 --- a/engines/sci/tools/scriptdump.c +++ /dev/null @@ -1,49 +0,0 @@ -/*************************************************************************** - 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/scriptdump.cpp b/engines/sci/tools/scriptdump.cpp new file mode 100644 index 0000000000..6efe150ba2 --- /dev/null +++ b/engines/sci/tools/scriptdump.cpp @@ -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 deleted file mode 100644 index 5dcf72a09b..0000000000 --- a/engines/sci/tools/vocabdump.c +++ /dev/null @@ -1,77 +0,0 @@ -/*************************************************************************** - 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 +#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